@betterstart/cli 0.1.0

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.
@@ -0,0 +1,223 @@
1
+ import {
2
+ detectPreset,
3
+ getDefaultConfig,
4
+ getPreset
5
+ } from "./chunk-PWRI4LKM.js";
6
+ import {
7
+ ConfigurationError,
8
+ getLogger
9
+ } from "./chunk-WY6BC55D.js";
10
+
11
+ // src/config/loader.ts
12
+ import fs from "fs";
13
+ import path from "path";
14
+ var CONFIG_FILE_NAMES = [
15
+ "betterstart.config.ts",
16
+ "betterstart.config.js",
17
+ "betterstart.config.mjs",
18
+ "betterstart.config.json",
19
+ ".betterstartrc.json",
20
+ ".betterstartrc"
21
+ ];
22
+ function findConfigFile(cwd) {
23
+ for (const fileName of CONFIG_FILE_NAMES) {
24
+ const filePath = path.join(cwd, fileName);
25
+ if (fs.existsSync(filePath)) {
26
+ return filePath;
27
+ }
28
+ }
29
+ return void 0;
30
+ }
31
+ async function loadConfigFile(configPath) {
32
+ const ext = path.extname(configPath);
33
+ const logger = getLogger();
34
+ try {
35
+ if (ext === ".json" || configPath.endsWith(".betterstartrc")) {
36
+ const content = fs.readFileSync(configPath, "utf-8");
37
+ return JSON.parse(content);
38
+ }
39
+ if (ext === ".ts" || ext === ".js" || ext === ".mjs") {
40
+ const configUrl = `file://${configPath}`;
41
+ const module = await import(configUrl);
42
+ return module.default || module;
43
+ }
44
+ throw new ConfigurationError(`Unsupported config file type: ${ext}`, { path: configPath });
45
+ } catch (error) {
46
+ if (error instanceof ConfigurationError) {
47
+ throw error;
48
+ }
49
+ logger.error(`Failed to load config file: ${configPath}`);
50
+ throw new ConfigurationError(
51
+ `Failed to load configuration from ${configPath}: ${error instanceof Error ? error.message : String(error)}`,
52
+ { path: configPath, originalError: error }
53
+ );
54
+ }
55
+ }
56
+ function deepMerge(base, override) {
57
+ const result = { ...base };
58
+ for (const key of Object.keys(override)) {
59
+ const baseValue = base[key];
60
+ const overrideValue = override[key];
61
+ if (overrideValue === void 0) {
62
+ continue;
63
+ }
64
+ if (typeof baseValue === "object" && baseValue !== null && !Array.isArray(baseValue) && typeof overrideValue === "object" && overrideValue !== null && !Array.isArray(overrideValue)) {
65
+ result[key] = deepMerge(
66
+ baseValue,
67
+ overrideValue
68
+ );
69
+ } else {
70
+ result[key] = overrideValue;
71
+ }
72
+ }
73
+ return result;
74
+ }
75
+ async function loadConfig(cwd, presetName) {
76
+ const workingDir = cwd || process.cwd();
77
+ const logger = getLogger();
78
+ let baseConfig;
79
+ if (presetName) {
80
+ const preset = getPreset(presetName);
81
+ if (!preset) {
82
+ throw new ConfigurationError(`Unknown preset: ${presetName}`, {
83
+ availablePresets: ["nextjs-monorepo", "nextjs-standalone", "custom"]
84
+ });
85
+ }
86
+ baseConfig = preset.config;
87
+ logger.debug(`Using preset: ${presetName}`);
88
+ } else {
89
+ baseConfig = getDefaultConfig(workingDir);
90
+ const detectedPreset = detectPreset(workingDir);
91
+ logger.debug(`Auto-detected preset: ${detectedPreset.name}`);
92
+ }
93
+ const configPath = findConfigFile(workingDir);
94
+ let userConfig = {};
95
+ if (configPath) {
96
+ logger.debug(`Loading config from: ${configPath}`);
97
+ userConfig = await loadConfigFile(configPath);
98
+ }
99
+ const mergedConfig = deepMerge(
100
+ baseConfig,
101
+ userConfig
102
+ );
103
+ return mergedConfig;
104
+ }
105
+ function findProjectRoot(startDir) {
106
+ let currentDir = path.resolve(startDir);
107
+ const _rootIndicators = ["pnpm-workspace.yaml", "turbo.json", "package.json"];
108
+ while (currentDir !== path.dirname(currentDir)) {
109
+ if (fs.existsSync(path.join(currentDir, "pnpm-workspace.yaml")) || fs.existsSync(path.join(currentDir, "turbo.json"))) {
110
+ return currentDir;
111
+ }
112
+ if (fs.existsSync(path.join(currentDir, "package.json"))) {
113
+ const parentDir = path.dirname(currentDir);
114
+ if (fs.existsSync(path.join(parentDir, "pnpm-workspace.yaml")) || fs.existsSync(path.join(parentDir, "turbo.json"))) {
115
+ return parentDir;
116
+ }
117
+ return currentDir;
118
+ }
119
+ currentDir = path.dirname(currentDir);
120
+ }
121
+ return path.resolve(startDir);
122
+ }
123
+ function resolvePaths(config, projectRoot) {
124
+ const root = path.resolve(projectRoot);
125
+ const appPath = path.resolve(root, config.paths.app);
126
+ const databasePath = path.resolve(root, config.paths.database);
127
+ const libPath = path.resolve(root, config.paths.lib);
128
+ const hooksPath = path.resolve(root, config.paths.hooks);
129
+ const schemasPath = path.resolve(root, config.paths.schemas);
130
+ const outputPaths = {
131
+ actions: path.resolve(libPath, config.paths.output.actions),
132
+ hooks: path.resolve(hooksPath, config.paths.output.hooks),
133
+ components: path.resolve(appPath, config.paths.output.components),
134
+ pages: path.resolve(appPath, config.paths.output.pages),
135
+ emails: path.resolve(appPath, config.paths.output.emails)
136
+ };
137
+ return {
138
+ root,
139
+ app: appPath,
140
+ database: databasePath,
141
+ lib: libPath,
142
+ hooks: hooksPath,
143
+ schemas: schemasPath,
144
+ output: outputPaths
145
+ };
146
+ }
147
+ function resolveConfig(config, projectRoot) {
148
+ const root = projectRoot || findProjectRoot(process.cwd());
149
+ const resolvedPaths = resolvePaths(config, root);
150
+ return {
151
+ ...config,
152
+ paths: resolvedPaths
153
+ };
154
+ }
155
+ function defineConfig(config) {
156
+ return config;
157
+ }
158
+ function validateConfig(config) {
159
+ const errors = [];
160
+ if (!config.paths) {
161
+ errors.push('Configuration must have a "paths" object');
162
+ } else {
163
+ if (!config.paths.app) errors.push("paths.app is required");
164
+ if (!config.paths.database) errors.push("paths.database is required");
165
+ if (!config.paths.lib) errors.push("paths.lib is required");
166
+ if (!config.paths.hooks) errors.push("paths.hooks is required");
167
+ if (!config.paths.schemas) errors.push("paths.schemas is required");
168
+ }
169
+ if (!config.database) {
170
+ errors.push('Configuration must have a "database" object');
171
+ } else {
172
+ if (config.database.provider !== "drizzle") {
173
+ errors.push('database.provider must be "drizzle"');
174
+ }
175
+ }
176
+ if (!config.ui) {
177
+ errors.push('Configuration must have a "ui" object');
178
+ } else {
179
+ if (config.ui.framework !== "shadcn") {
180
+ errors.push('ui.framework must be "shadcn"');
181
+ }
182
+ }
183
+ return errors;
184
+ }
185
+ function checkPaths(paths) {
186
+ const exists = (p) => {
187
+ try {
188
+ fs.accessSync(p);
189
+ return true;
190
+ } catch {
191
+ return false;
192
+ }
193
+ };
194
+ return {
195
+ root: exists(paths.root),
196
+ app: exists(paths.app),
197
+ database: exists(paths.database),
198
+ lib: exists(paths.lib),
199
+ hooks: exists(paths.hooks),
200
+ schemas: exists(paths.schemas),
201
+ output: {
202
+ actions: exists(paths.output.actions),
203
+ hooks: exists(paths.output.hooks),
204
+ components: exists(paths.output.components),
205
+ pages: exists(paths.output.pages),
206
+ emails: exists(paths.output.emails)
207
+ }
208
+ };
209
+ }
210
+
211
+ export {
212
+ CONFIG_FILE_NAMES,
213
+ findConfigFile,
214
+ loadConfigFile,
215
+ loadConfig,
216
+ findProjectRoot,
217
+ resolvePaths,
218
+ resolveConfig,
219
+ defineConfig,
220
+ validateConfig,
221
+ checkPaths
222
+ };
223
+ //# sourceMappingURL=chunk-46UVIUJF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/loader.ts"],"sourcesContent":["/**\n * Configuration loader for @betterstart/cli\n * Handles loading, merging, and resolving configuration\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { ConfigurationError } from '../core/errors'\nimport { getLogger } from '../core/logger'\nimport { detectPreset, getDefaultConfig, getPreset } from './presets'\nimport type { BetterstartConfig, ResolvedConfig, ResolvedPaths, UserConfig } from './types'\n\n// ============================================================================\n// Configuration File Names\n// ============================================================================\n\n/**\n * Supported configuration file names (in priority order)\n */\nexport const CONFIG_FILE_NAMES = [\n 'betterstart.config.ts',\n 'betterstart.config.js',\n 'betterstart.config.mjs',\n 'betterstart.config.json',\n '.betterstartrc.json',\n '.betterstartrc'\n]\n\n// ============================================================================\n// Configuration Loading\n// ============================================================================\n\n/**\n * Find the configuration file in a directory\n * @param cwd - Directory to search in\n * @returns Path to the config file or undefined if not found\n */\nexport function findConfigFile(cwd: string): string | undefined {\n for (const fileName of CONFIG_FILE_NAMES) {\n const filePath = path.join(cwd, fileName)\n if (fs.existsSync(filePath)) {\n return filePath\n }\n }\n return undefined\n}\n\n/**\n * Load configuration from a file\n * @param configPath - Path to the configuration file\n * @returns The loaded configuration\n */\nexport async function loadConfigFile(configPath: string): Promise<UserConfig> {\n const ext = path.extname(configPath)\n const logger = getLogger()\n\n try {\n if (ext === '.json' || configPath.endsWith('.betterstartrc')) {\n // Load JSON config\n const content = fs.readFileSync(configPath, 'utf-8')\n return JSON.parse(content) as UserConfig\n }\n\n if (ext === '.ts' || ext === '.js' || ext === '.mjs') {\n // Load TypeScript/JavaScript config using dynamic import\n // This requires tsx or ts-node to be available for .ts files\n const configUrl = `file://${configPath}`\n const module = await import(configUrl)\n return (module.default || module) as UserConfig\n }\n\n throw new ConfigurationError(`Unsupported config file type: ${ext}`, { path: configPath })\n } catch (error) {\n if (error instanceof ConfigurationError) {\n throw error\n }\n logger.error(`Failed to load config file: ${configPath}`)\n throw new ConfigurationError(\n `Failed to load configuration from ${configPath}: ${error instanceof Error ? error.message : String(error)}`,\n { path: configPath, originalError: error }\n )\n }\n}\n\n/**\n * Deep merge two configuration objects\n * Arrays are replaced, not merged\n */\nfunction deepMerge<T extends Record<string, unknown>>(base: T, override: Partial<T>): T {\n const result = { ...base }\n\n for (const key of Object.keys(override) as Array<keyof T>) {\n const baseValue = base[key]\n const overrideValue = override[key]\n\n if (overrideValue === undefined) {\n continue\n }\n\n if (\n typeof baseValue === 'object' &&\n baseValue !== null &&\n !Array.isArray(baseValue) &&\n typeof overrideValue === 'object' &&\n overrideValue !== null &&\n !Array.isArray(overrideValue)\n ) {\n // Recursively merge objects\n result[key] = deepMerge(\n baseValue as Record<string, unknown>,\n overrideValue as Record<string, unknown>\n ) as T[keyof T]\n } else {\n // Replace arrays and primitives\n result[key] = overrideValue as T[keyof T]\n }\n }\n\n return result\n}\n\n/**\n * Load configuration from a directory\n * Automatically finds and loads the config file, or uses defaults\n *\n * @param cwd - Directory to load config from (defaults to process.cwd())\n * @param presetName - Optional preset name to use as base\n * @returns The loaded and merged configuration\n */\nexport async function loadConfig(cwd?: string, presetName?: string): Promise<BetterstartConfig> {\n const workingDir = cwd || process.cwd()\n const logger = getLogger()\n\n // Get base configuration from preset or auto-detection\n let baseConfig: UserConfig\n if (presetName) {\n const preset = getPreset(presetName)\n if (!preset) {\n throw new ConfigurationError(`Unknown preset: ${presetName}`, {\n availablePresets: ['nextjs-monorepo', 'nextjs-standalone', 'custom']\n })\n }\n baseConfig = preset.config\n logger.debug(`Using preset: ${presetName}`)\n } else {\n baseConfig = getDefaultConfig(workingDir)\n const detectedPreset = detectPreset(workingDir)\n logger.debug(`Auto-detected preset: ${detectedPreset.name}`)\n }\n\n // Look for config file\n const configPath = findConfigFile(workingDir)\n let userConfig: UserConfig = {}\n\n if (configPath) {\n logger.debug(`Loading config from: ${configPath}`)\n userConfig = await loadConfigFile(configPath)\n }\n\n // Merge configurations\n const mergedConfig = deepMerge(\n baseConfig as Record<string, unknown>,\n userConfig as Record<string, unknown>\n )\n\n return mergedConfig as unknown as BetterstartConfig\n}\n\n// ============================================================================\n// Path Resolution\n// ============================================================================\n\n/**\n * Find the project root directory\n * Looks for common root indicators (package.json, pnpm-workspace.yaml, turbo.json)\n *\n * @param startDir - Directory to start searching from\n * @returns The project root directory\n */\nexport function findProjectRoot(startDir: string): string {\n let currentDir = path.resolve(startDir)\n const _rootIndicators = ['pnpm-workspace.yaml', 'turbo.json', 'package.json']\n\n // Walk up the directory tree\n while (currentDir !== path.dirname(currentDir)) {\n // Check for monorepo root indicators first\n if (\n fs.existsSync(path.join(currentDir, 'pnpm-workspace.yaml')) ||\n fs.existsSync(path.join(currentDir, 'turbo.json'))\n ) {\n return currentDir\n }\n\n // Check for package.json\n if (fs.existsSync(path.join(currentDir, 'package.json'))) {\n // If there's a package.json but no monorepo indicators,\n // check if parent has monorepo indicators\n const parentDir = path.dirname(currentDir)\n if (\n fs.existsSync(path.join(parentDir, 'pnpm-workspace.yaml')) ||\n fs.existsSync(path.join(parentDir, 'turbo.json'))\n ) {\n return parentDir\n }\n // Otherwise, this package.json is our root\n return currentDir\n }\n\n currentDir = path.dirname(currentDir)\n }\n\n // Fallback to start directory\n return path.resolve(startDir)\n}\n\n/**\n * Resolve configuration paths to absolute paths\n *\n * @param config - The configuration to resolve\n * @param projectRoot - The project root directory\n * @returns Configuration with resolved absolute paths\n */\nexport function resolvePaths(config: BetterstartConfig, projectRoot: string): ResolvedPaths {\n const root = path.resolve(projectRoot)\n\n // Resolve main paths\n const appPath = path.resolve(root, config.paths.app)\n const databasePath = path.resolve(root, config.paths.database)\n const libPath = path.resolve(root, config.paths.lib)\n const hooksPath = path.resolve(root, config.paths.hooks)\n const schemasPath = path.resolve(root, config.paths.schemas)\n\n // Resolve output paths relative to their parent packages\n const outputPaths = {\n actions: path.resolve(libPath, config.paths.output.actions),\n hooks: path.resolve(hooksPath, config.paths.output.hooks),\n components: path.resolve(appPath, config.paths.output.components),\n pages: path.resolve(appPath, config.paths.output.pages),\n emails: path.resolve(appPath, config.paths.output.emails)\n }\n\n return {\n root,\n app: appPath,\n database: databasePath,\n lib: libPath,\n hooks: hooksPath,\n schemas: schemasPath,\n output: outputPaths\n }\n}\n\n/**\n * Fully resolve configuration including paths\n *\n * @param config - The configuration to resolve\n * @param projectRoot - The project root directory (optional, auto-detected if not provided)\n * @returns Fully resolved configuration\n */\nexport function resolveConfig(config: BetterstartConfig, projectRoot?: string): ResolvedConfig {\n const root = projectRoot || findProjectRoot(process.cwd())\n const resolvedPaths = resolvePaths(config, root)\n\n return {\n ...config,\n paths: resolvedPaths\n }\n}\n\n// ============================================================================\n// Configuration Helper\n// ============================================================================\n\n/**\n * Helper function for defining configuration in betterstart.config.ts\n * Provides type checking and autocomplete\n *\n * @example\n * ```ts\n * // betterstart.config.ts\n * import { defineConfig } from '@betterstart/cli'\n *\n * export default defineConfig({\n * paths: {\n * app: 'apps/web',\n * database: 'packages/database'\n * }\n * })\n * ```\n */\nexport function defineConfig(config: UserConfig): UserConfig {\n return config\n}\n\n// ============================================================================\n// Validation\n// ============================================================================\n\n/**\n * Validate a configuration\n * @param config - The configuration to validate\n * @returns Array of validation errors (empty if valid)\n */\nexport function validateConfig(config: BetterstartConfig): string[] {\n const errors: string[] = []\n\n // Validate paths\n if (!config.paths) {\n errors.push('Configuration must have a \"paths\" object')\n } else {\n if (!config.paths.app) errors.push('paths.app is required')\n if (!config.paths.database) errors.push('paths.database is required')\n if (!config.paths.lib) errors.push('paths.lib is required')\n if (!config.paths.hooks) errors.push('paths.hooks is required')\n if (!config.paths.schemas) errors.push('paths.schemas is required')\n }\n\n // Validate database config\n if (!config.database) {\n errors.push('Configuration must have a \"database\" object')\n } else {\n if (config.database.provider !== 'drizzle') {\n errors.push('database.provider must be \"drizzle\"')\n }\n }\n\n // Validate UI config\n if (!config.ui) {\n errors.push('Configuration must have a \"ui\" object')\n } else {\n if (config.ui.framework !== 'shadcn') {\n errors.push('ui.framework must be \"shadcn\"')\n }\n }\n\n return errors\n}\n\n/**\n * Check if resolved paths exist and are accessible\n * @param paths - The resolved paths to check\n * @returns Object with path existence status\n */\nexport function checkPaths(paths: ResolvedPaths): Record<keyof ResolvedPaths, boolean> {\n const exists = (p: string) => {\n try {\n fs.accessSync(p)\n return true\n } catch {\n return false\n }\n }\n\n return {\n root: exists(paths.root),\n app: exists(paths.app),\n database: exists(paths.database),\n lib: exists(paths.lib),\n hooks: exists(paths.hooks),\n schemas: exists(paths.schemas),\n output: {\n actions: exists(paths.output.actions),\n hooks: exists(paths.output.hooks),\n components: exists(paths.output.components),\n pages: exists(paths.output.pages),\n emails: exists(paths.output.emails)\n }\n } as unknown as Record<keyof ResolvedPaths, boolean>\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,179 @@
1
+ import {
2
+ ImportResolver,
3
+ MONOREPO_IMPORT_PATHS
4
+ } from "./chunk-PWRI4LKM.js";
5
+
6
+ // src/utils.ts
7
+ import fs from "fs";
8
+ import path from "path";
9
+ var _paths = null;
10
+ function initPaths(startDir) {
11
+ const cwd = startDir || process.cwd();
12
+ let root = cwd;
13
+ while (root !== "/") {
14
+ if (fs.existsSync(path.join(root, "turbo.json")) || fs.existsSync(path.join(root, "pnpm-workspace.yaml"))) {
15
+ break;
16
+ }
17
+ root = path.dirname(root);
18
+ }
19
+ if (root === "/") {
20
+ throw new Error("Could not find monorepo root (no turbo.json or pnpm-workspace.yaml found)");
21
+ }
22
+ _paths = {
23
+ root,
24
+ app: path.join(root, "apps/web"),
25
+ database: path.join(root, "packages/database"),
26
+ lib: path.join(root, "packages/lib"),
27
+ hooks: path.join(root, "packages/hooks"),
28
+ schemas: path.join(root, "packages/codegen/src/schemas"),
29
+ data: path.join(root, "packages/data")
30
+ };
31
+ return _paths;
32
+ }
33
+ function getPaths() {
34
+ if (!_paths) {
35
+ return initPaths();
36
+ }
37
+ return _paths;
38
+ }
39
+ function getProjectRoot() {
40
+ return getPaths().app;
41
+ }
42
+ var _importResolver = null;
43
+ function initImportResolver(config) {
44
+ _importResolver = new ImportResolver(config.imports);
45
+ return _importResolver;
46
+ }
47
+ function getImportResolver() {
48
+ if (!_importResolver) {
49
+ _importResolver = new ImportResolver(MONOREPO_IMPORT_PATHS);
50
+ }
51
+ return _importResolver;
52
+ }
53
+ function toPascalCase(str) {
54
+ return str.split(/[-_\s]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
55
+ }
56
+ function toCamelCase(str) {
57
+ const pascal = toPascalCase(str);
58
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
59
+ }
60
+ function toKebabCase(str) {
61
+ return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
62
+ }
63
+ function toSnakeCase(str) {
64
+ return str.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[\s-]+/g, "_").toLowerCase();
65
+ }
66
+ function toScreamingSnakeCase(str) {
67
+ return str.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[\s-]+/g, "_").toUpperCase();
68
+ }
69
+ function ensureDir(dirPath) {
70
+ if (!fs.existsSync(dirPath)) {
71
+ fs.mkdirSync(dirPath, { recursive: true });
72
+ }
73
+ }
74
+ function getMigrationTimestamp() {
75
+ const now = /* @__PURE__ */ new Date();
76
+ const year = now.getFullYear();
77
+ const month = String(now.getMonth() + 1).padStart(2, "0");
78
+ const day = String(now.getDate()).padStart(2, "0");
79
+ const hours = String(now.getHours()).padStart(2, "0");
80
+ const minutes = String(now.getMinutes()).padStart(2, "0");
81
+ const seconds = String(now.getSeconds()).padStart(2, "0");
82
+ return `${year}${month}${day}${hours}${minutes}${seconds}`;
83
+ }
84
+ function isPlural(str) {
85
+ const pluralPatterns = [
86
+ /ies$/,
87
+ /ves$/,
88
+ /ses$/,
89
+ /xes$/,
90
+ /zes$/,
91
+ /ches$/,
92
+ /shes$/,
93
+ /men$/,
94
+ /people$/
95
+ ];
96
+ for (const pattern of pluralPatterns) {
97
+ if (pattern.test(str)) {
98
+ return true;
99
+ }
100
+ }
101
+ if (str.endsWith("s") && !str.endsWith("ss")) {
102
+ return true;
103
+ }
104
+ return false;
105
+ }
106
+ function pluralize(str) {
107
+ if (isPlural(str)) {
108
+ return str;
109
+ }
110
+ if (str.endsWith("y") && !["ay", "ey", "iy", "oy", "uy"].some((v) => str.endsWith(v))) {
111
+ return `${str.slice(0, -1)}ies`;
112
+ }
113
+ if (str.endsWith("s") || str.endsWith("x") || str.endsWith("ch") || str.endsWith("sh")) {
114
+ return `${str}es`;
115
+ }
116
+ if (str.endsWith("f")) {
117
+ return `${str.slice(0, -1)}ves`;
118
+ }
119
+ if (str.endsWith("fe")) {
120
+ return `${str.slice(0, -2)}ves`;
121
+ }
122
+ return `${str}s`;
123
+ }
124
+ function singularize(str) {
125
+ if (!isPlural(str)) {
126
+ return str;
127
+ }
128
+ if (str.endsWith("ies")) {
129
+ return `${str.slice(0, -3)}y`;
130
+ }
131
+ if (str.endsWith("ves")) {
132
+ return `${str.slice(0, -3)}f`;
133
+ }
134
+ if (str.endsWith("sses") || str.endsWith("xes") || str.endsWith("ches") || str.endsWith("shes") || str.endsWith("zes")) {
135
+ return str.slice(0, -2);
136
+ }
137
+ if (str.endsWith("s") && !str.endsWith("ss")) {
138
+ return str.slice(0, -1);
139
+ }
140
+ return str;
141
+ }
142
+ function quotePropertyName(name) {
143
+ const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
144
+ if (validIdentifier.test(name)) {
145
+ return name;
146
+ }
147
+ return `'${name}'`;
148
+ }
149
+ function singularizeLabel(label) {
150
+ const words = label.split(" ");
151
+ if (words.length === 0) {
152
+ return label;
153
+ }
154
+ const lastWord = words[words.length - 1];
155
+ const lastWordLower = lastWord.toLowerCase();
156
+ const singularLastWordLower = singularize(lastWordLower);
157
+ const capitalizedSingular = lastWord[0] === lastWord[0].toUpperCase() ? singularLastWordLower.charAt(0).toUpperCase() + singularLastWordLower.slice(1) : singularLastWordLower;
158
+ return [...words.slice(0, -1), capitalizedSingular].join(" ");
159
+ }
160
+
161
+ export {
162
+ initPaths,
163
+ getPaths,
164
+ getProjectRoot,
165
+ initImportResolver,
166
+ getImportResolver,
167
+ toPascalCase,
168
+ toCamelCase,
169
+ toKebabCase,
170
+ toSnakeCase,
171
+ toScreamingSnakeCase,
172
+ ensureDir,
173
+ getMigrationTimestamp,
174
+ pluralize,
175
+ singularize,
176
+ quotePropertyName,
177
+ singularizeLabel
178
+ };
179
+ //# sourceMappingURL=chunk-GEH43BA4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils.ts"],"sourcesContent":["// Utility functions for the codemod system\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { ImportResolver } from './config/import-resolver'\nimport { MONOREPO_IMPORT_PATHS } from './config/presets'\nimport type { BetterstartConfig } from './config/types'\nimport type { MonorepoPaths } from './types'\n\n// Store monorepo paths globally (set during CLI initialization)\nlet _paths: MonorepoPaths | null = null\n\n/**\n * Initialize paths for the monorepo\n * Detects monorepo root and sets up all package paths\n */\nexport function initPaths(startDir?: string): MonorepoPaths {\n const cwd = startDir || process.cwd()\n\n // Find monorepo root by looking for turbo.json or pnpm-workspace.yaml\n let root = cwd\n while (root !== '/') {\n if (\n fs.existsSync(path.join(root, 'turbo.json')) ||\n fs.existsSync(path.join(root, 'pnpm-workspace.yaml'))\n ) {\n break\n }\n root = path.dirname(root)\n }\n\n if (root === '/') {\n throw new Error('Could not find monorepo root (no turbo.json or pnpm-workspace.yaml found)')\n }\n\n _paths = {\n root,\n app: path.join(root, 'apps/web'),\n database: path.join(root, 'packages/database'),\n lib: path.join(root, 'packages/lib'),\n hooks: path.join(root, 'packages/hooks'),\n schemas: path.join(root, 'packages/codegen/src/schemas'),\n data: path.join(root, 'packages/data')\n }\n\n return _paths\n}\n\n/**\n * Get monorepo paths (must call initPaths first)\n */\nexport function getPaths(): MonorepoPaths {\n if (!_paths) {\n return initPaths()\n }\n return _paths\n}\n\n/**\n * Get the monorepo root directory\n * @deprecated Use getPaths().root instead\n */\nexport function getProjectRoot(): string {\n return getPaths().app\n}\n\n// Store import resolver globally (set during CLI initialization)\nlet _importResolver: ImportResolver | null = null\n\n/**\n * Initialize the import resolver from config\n */\nexport function initImportResolver(config: BetterstartConfig): ImportResolver {\n _importResolver = new ImportResolver(config.imports)\n return _importResolver\n}\n\n/**\n * Get the import resolver (must call initImportResolver first, or falls back to monorepo defaults)\n */\nexport function getImportResolver(): ImportResolver {\n if (!_importResolver) {\n _importResolver = new ImportResolver(MONOREPO_IMPORT_PATHS)\n }\n return _importResolver\n}\n\n/**\n * Convert a string to PascalCase\n */\nexport function toPascalCase(str: string): string {\n return str\n .split(/[-_\\s]+/)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join('')\n}\n\n/**\n * Convert a string to camelCase\n */\nexport function toCamelCase(str: string): string {\n const pascal = toPascalCase(str)\n return pascal.charAt(0).toLowerCase() + pascal.slice(1)\n}\n\n/**\n * Convert a string to kebab-case\n */\nexport function toKebabCase(str: string): string {\n return str\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .toLowerCase()\n}\n\n/**\n * Convert a string to snake_case\n */\nexport function toSnakeCase(str: string): string {\n return str\n .replace(/([a-z])([A-Z])/g, '$1_$2')\n .replace(/[\\s-]+/g, '_')\n .toLowerCase()\n}\n\n/**\n * Convert a string to SCREAMING_SNAKE_CASE\n */\nexport function toScreamingSnakeCase(str: string): string {\n return str\n .replace(/([a-z])([A-Z])/g, '$1_$2')\n .replace(/[\\s-]+/g, '_')\n .toUpperCase()\n}\n\n/**\n * Ensure a directory exists, create it if it doesn't\n */\nexport function ensureDir(dirPath: string): void {\n if (!fs.existsSync(dirPath)) {\n fs.mkdirSync(dirPath, { recursive: true })\n }\n}\n\n/**\n * Format a timestamp for migration files\n */\nexport function getMigrationTimestamp(): string {\n const now = new Date()\n const year = now.getFullYear()\n const month = String(now.getMonth() + 1).padStart(2, '0')\n const day = String(now.getDate()).padStart(2, '0')\n const hours = String(now.getHours()).padStart(2, '0')\n const minutes = String(now.getMinutes()).padStart(2, '0')\n const seconds = String(now.getSeconds()).padStart(2, '0')\n return `${year}${month}${day}${hours}${minutes}${seconds}`\n}\n\n/**\n * Check if a word is already plural\n */\nfunction isPlural(str: string): boolean {\n const pluralPatterns = [\n /ies$/,\n /ves$/,\n /ses$/,\n /xes$/,\n /zes$/,\n /ches$/,\n /shes$/,\n /men$/,\n /people$/\n ]\n\n for (const pattern of pluralPatterns) {\n if (pattern.test(str)) {\n return true\n }\n }\n\n if (str.endsWith('s') && !str.endsWith('ss')) {\n return true\n }\n\n return false\n}\n\n/**\n * Pluralize a string\n */\nexport function pluralize(str: string): string {\n if (isPlural(str)) {\n return str\n }\n\n if (str.endsWith('y') && !['ay', 'ey', 'iy', 'oy', 'uy'].some((v) => str.endsWith(v))) {\n return `${str.slice(0, -1)}ies`\n }\n if (str.endsWith('s') || str.endsWith('x') || str.endsWith('ch') || str.endsWith('sh')) {\n return `${str}es`\n }\n if (str.endsWith('f')) {\n return `${str.slice(0, -1)}ves`\n }\n if (str.endsWith('fe')) {\n return `${str.slice(0, -2)}ves`\n }\n return `${str}s`\n}\n\n/**\n * Singularize a string\n */\nexport function singularize(str: string): string {\n if (!isPlural(str)) {\n return str\n }\n\n if (str.endsWith('ies')) {\n return `${str.slice(0, -3)}y`\n }\n if (str.endsWith('ves')) {\n return `${str.slice(0, -3)}f`\n }\n if (\n str.endsWith('sses') ||\n str.endsWith('xes') ||\n str.endsWith('ches') ||\n str.endsWith('shes') ||\n str.endsWith('zes')\n ) {\n return str.slice(0, -2)\n }\n if (str.endsWith('s') && !str.endsWith('ss')) {\n return str.slice(0, -1)\n }\n return str\n}\n\n/**\n * Quote a property name if it's not a valid JavaScript identifier.\n * Properties with hyphens, spaces, or starting with numbers need quotes.\n */\nexport function quotePropertyName(name: string): string {\n // Valid JS identifier: starts with letter/underscore/$, contains only letters/numbers/underscore/$\n const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/\n if (validIdentifier.test(name)) {\n return name\n }\n return `'${name}'`\n}\n\n/**\n * Singularize a label (e.g., \"Trusted Companies\" -> \"Trusted Company\")\n * Handles display labels with proper capitalization\n */\nexport function singularizeLabel(label: string): string {\n const words = label.split(' ')\n if (words.length === 0) {\n return label\n }\n\n // Singularize the last word\n const lastWord = words[words.length - 1]\n const lastWordLower = lastWord.toLowerCase()\n const singularLastWordLower = singularize(lastWordLower)\n\n // Preserve capitalization of the last word\n const capitalizedSingular =\n lastWord[0] === lastWord[0].toUpperCase()\n ? singularLastWordLower.charAt(0).toUpperCase() + singularLastWordLower.slice(1)\n : singularLastWordLower\n\n return [...words.slice(0, -1), capitalizedSingular].join(' ')\n}\n"],"mappings":"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","names":[]}