create-nyoworks 2.7.1 → 3.0.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.
- package/dist/config-loader.d.ts +45 -0
- package/dist/config-loader.js +68 -0
- package/dist/init.js +221 -107
- package/package.json +3 -2
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export interface AppDefinition {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
name_tr: string;
|
|
5
|
+
description: string;
|
|
6
|
+
description_tr: string;
|
|
7
|
+
platforms: ("web" | "mobile" | "desktop")[];
|
|
8
|
+
features: string[];
|
|
9
|
+
integrations: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface IntegrationDefinition {
|
|
12
|
+
name: string;
|
|
13
|
+
providers?: string[];
|
|
14
|
+
default?: string;
|
|
15
|
+
services?: string[];
|
|
16
|
+
free_tier?: Record<string, string>;
|
|
17
|
+
note?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface PlatformDefinition {
|
|
20
|
+
name: string;
|
|
21
|
+
framework: string;
|
|
22
|
+
version: string;
|
|
23
|
+
}
|
|
24
|
+
export interface AppsConfig {
|
|
25
|
+
apps: AppDefinition[];
|
|
26
|
+
integrations: Record<string, IntegrationDefinition>;
|
|
27
|
+
platforms: Record<string, PlatformDefinition>;
|
|
28
|
+
}
|
|
29
|
+
export declare function loadAppsConfig(repoDir: string): Promise<AppsConfig>;
|
|
30
|
+
export declare function appToPromptChoice(app: AppDefinition, lang?: string): {
|
|
31
|
+
title: string;
|
|
32
|
+
value: string;
|
|
33
|
+
description: string;
|
|
34
|
+
};
|
|
35
|
+
export declare function integrationToPromptChoices(integration: IntegrationDefinition, integrationId: string): {
|
|
36
|
+
choices: {
|
|
37
|
+
title: string;
|
|
38
|
+
value: string;
|
|
39
|
+
}[];
|
|
40
|
+
initial: number;
|
|
41
|
+
message: string;
|
|
42
|
+
} | null;
|
|
43
|
+
export declare function getAppFeatures(app: AppDefinition): string[];
|
|
44
|
+
export declare function getAppIntegrations(app: AppDefinition): string[];
|
|
45
|
+
export declare function getAppPlatforms(app: AppDefinition): string[];
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// Config Loader - Load app definitions from YAML
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
4
|
+
import fs from "fs-extra";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { parse } from "yaml";
|
|
7
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
8
|
+
// Loader Functions
|
|
9
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
10
|
+
export async function loadAppsConfig(repoDir) {
|
|
11
|
+
const configPath = path.join(repoDir, "config", "apps.yaml");
|
|
12
|
+
if (!await fs.pathExists(configPath)) {
|
|
13
|
+
throw new Error("config/apps.yaml not found in repository");
|
|
14
|
+
}
|
|
15
|
+
const content = await fs.readFile(configPath, "utf8");
|
|
16
|
+
return parse(content);
|
|
17
|
+
}
|
|
18
|
+
export function appToPromptChoice(app, lang = "en") {
|
|
19
|
+
const name = lang === "tr" ? app.name_tr : app.name;
|
|
20
|
+
const desc = lang === "tr" ? app.description_tr : app.description;
|
|
21
|
+
return {
|
|
22
|
+
title: name,
|
|
23
|
+
value: app.id,
|
|
24
|
+
description: desc,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function integrationToPromptChoices(integration, integrationId) {
|
|
28
|
+
if (!integration.providers || integration.providers.length <= 1) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const defaultIndex = integration.providers.indexOf(integration.default || integration.providers[0]);
|
|
32
|
+
return {
|
|
33
|
+
choices: integration.providers.map((provider) => ({
|
|
34
|
+
title: formatProviderName(provider),
|
|
35
|
+
value: provider,
|
|
36
|
+
})),
|
|
37
|
+
initial: defaultIndex >= 0 ? defaultIndex : 0,
|
|
38
|
+
message: `${integration.name} provider:`,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function formatProviderName(provider) {
|
|
42
|
+
const names = {
|
|
43
|
+
mollie: "Mollie (iDEAL, Bancontact)",
|
|
44
|
+
adyen: "Adyen",
|
|
45
|
+
stripe: "Stripe",
|
|
46
|
+
resend: "Resend (3000/ay free)",
|
|
47
|
+
brevo: "Brevo (300/gün free)",
|
|
48
|
+
twilio: "Twilio",
|
|
49
|
+
plivo: "Plivo",
|
|
50
|
+
postnl: "PostNL",
|
|
51
|
+
sendcloud: "Sendcloud",
|
|
52
|
+
dhl: "DHL",
|
|
53
|
+
storecove: "Storecove",
|
|
54
|
+
};
|
|
55
|
+
return names[provider] || provider.charAt(0).toUpperCase() + provider.slice(1);
|
|
56
|
+
}
|
|
57
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
58
|
+
// Feature Helpers
|
|
59
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
60
|
+
export function getAppFeatures(app) {
|
|
61
|
+
return app.features || [];
|
|
62
|
+
}
|
|
63
|
+
export function getAppIntegrations(app) {
|
|
64
|
+
return app.integrations || [];
|
|
65
|
+
}
|
|
66
|
+
export function getAppPlatforms(app) {
|
|
67
|
+
return app.platforms || ["web"];
|
|
68
|
+
}
|
package/dist/init.js
CHANGED
|
@@ -1,47 +1,23 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// Create NYOWORKS - Project Initialization
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1
4
|
import prompts from "prompts";
|
|
2
5
|
import pc from "picocolors";
|
|
3
6
|
import fs from "fs-extra";
|
|
4
7
|
import path from "path";
|
|
5
8
|
import os from "os";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
6
10
|
import { execa } from "execa";
|
|
7
11
|
import { replacePlaceholders } from "./replace.js";
|
|
8
12
|
import { checkDependencies, showClaudeMaxWarning, getDockerComposeCommand } from "./checks.js";
|
|
13
|
+
import { loadAppsConfig, appToPromptChoice, integrationToPromptChoices, getAppFeatures, getAppPlatforms, } from "./config-loader.js";
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = path.dirname(__filename);
|
|
16
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
17
|
+
// Constants
|
|
18
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
9
19
|
const REPO = "naimozcan/nyoworks-framework";
|
|
10
20
|
const BRANCH = "main";
|
|
11
|
-
const PRODUCT_TYPES = [
|
|
12
|
-
{ title: "E-Commerce", value: "ecommerce", description: "Online store, product sales" },
|
|
13
|
-
{ title: "Booking", value: "booking", description: "Appointment/reservation system" },
|
|
14
|
-
{ title: "SaaS Platform", value: "saas", description: "Subscription-based software" },
|
|
15
|
-
{ title: "Marketplace", value: "marketplace", description: "Multi-vendor platform" },
|
|
16
|
-
{ title: "Content/Blog", value: "content", description: "Blog, news, CMS" },
|
|
17
|
-
{ title: "CRM", value: "crm", description: "Customer relationship management" },
|
|
18
|
-
{ title: "Custom", value: "custom", description: "Manual feature selection" },
|
|
19
|
-
];
|
|
20
|
-
const REQUIRED_FEATURES = {
|
|
21
|
-
ecommerce: ["payments", "crm", "notifications", "search", "storage"],
|
|
22
|
-
booking: ["appointments", "payments", "crm", "notifications"],
|
|
23
|
-
saas: ["payments", "subscriptions", "analytics", "notifications", "audit", "multitenant"],
|
|
24
|
-
marketplace: ["payments", "crm", "analytics", "notifications", "audit", "search", "storage", "multitenant"],
|
|
25
|
-
content: ["analytics", "search", "storage"],
|
|
26
|
-
crm: ["crm", "analytics", "notifications", "audit", "export"],
|
|
27
|
-
custom: [],
|
|
28
|
-
};
|
|
29
|
-
const AVAILABLE_FEATURES = [
|
|
30
|
-
{ title: "Analytics", value: "analytics", description: "User behavior tracking" },
|
|
31
|
-
{ title: "Appointments", value: "appointments", description: "Booking system" },
|
|
32
|
-
{ title: "Audit", value: "audit", description: "Activity logging" },
|
|
33
|
-
{ title: "Auth Social", value: "auth-social", description: "Google, Apple, GitHub OAuth" },
|
|
34
|
-
{ title: "CRM", value: "crm", description: "Customer relationship management" },
|
|
35
|
-
{ title: "Export", value: "export", description: "PDF/CSV export" },
|
|
36
|
-
{ title: "i18n", value: "i18n", description: "Multi-language support" },
|
|
37
|
-
{ title: "Multitenant", value: "multitenant", description: "Multi-organization support" },
|
|
38
|
-
{ title: "Notifications", value: "notifications", description: "Email, SMS, Push" },
|
|
39
|
-
{ title: "Payments", value: "payments", description: "Stripe integration" },
|
|
40
|
-
{ title: "Realtime", value: "realtime", description: "WebSocket support" },
|
|
41
|
-
{ title: "Search", value: "search", description: "Full-text search" },
|
|
42
|
-
{ title: "Storage", value: "storage", description: "File uploads (S3/R2)" },
|
|
43
|
-
{ title: "Subscriptions", value: "subscriptions", description: "Plans & usage limits" },
|
|
44
|
-
];
|
|
45
21
|
const AVAILABLE_PLATFORMS = [
|
|
46
22
|
{ title: "Web", value: "web", description: "Next.js 16" },
|
|
47
23
|
{ title: "Mobile", value: "mobile", description: "Expo SDK 54" },
|
|
@@ -57,6 +33,16 @@ const LANGUAGE_RESPONSES = {
|
|
|
57
33
|
en: "English",
|
|
58
34
|
nl: "Dutch",
|
|
59
35
|
};
|
|
36
|
+
const ADDITIONAL_FEATURES = [
|
|
37
|
+
{ title: "Realtime", value: "realtime", description: "WebSocket support" },
|
|
38
|
+
{ title: "i18n", value: "i18n", description: "Multi-language support" },
|
|
39
|
+
{ title: "Auth Social", value: "auth-social", description: "Google, Apple, GitHub OAuth" },
|
|
40
|
+
{ title: "Multitenant", value: "multitenant", description: "Multi-organization support" },
|
|
41
|
+
{ title: "Subscriptions", value: "subscriptions", description: "Plans & usage limits" },
|
|
42
|
+
];
|
|
43
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
44
|
+
// Helper Functions
|
|
45
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
60
46
|
function generateCode(name) {
|
|
61
47
|
return name
|
|
62
48
|
.toUpperCase()
|
|
@@ -84,13 +70,44 @@ async function downloadRepo(repo, branch) {
|
|
|
84
70
|
await execa("tar", ["-xzf", tarFile, "-C", tempDir]);
|
|
85
71
|
return path.join(tempDir, `nyoworks-framework-${branch}`);
|
|
86
72
|
}
|
|
73
|
+
function getLocalRepoPath() {
|
|
74
|
+
const localPath = path.resolve(__dirname, "..", "..", "..");
|
|
75
|
+
const configPath = path.join(localPath, "config", "apps.yaml");
|
|
76
|
+
if (fs.existsSync(configPath)) {
|
|
77
|
+
return localPath;
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
async function replacePackageNames(targetDir, slug) {
|
|
82
|
+
const pkgFiles = await findPackageJsonFiles(targetDir);
|
|
83
|
+
for (const pkgFile of pkgFiles) {
|
|
84
|
+
let content = await fs.readFile(pkgFile, "utf8");
|
|
85
|
+
content = content.replace(/@nyoworks\//g, `@${slug}/`);
|
|
86
|
+
await fs.writeFile(pkgFile, content);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async function findPackageJsonFiles(dir) {
|
|
90
|
+
const results = [];
|
|
91
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
92
|
+
for (const entry of entries) {
|
|
93
|
+
const fullPath = path.join(dir, entry.name);
|
|
94
|
+
if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".git") {
|
|
95
|
+
results.push(...await findPackageJsonFiles(fullPath));
|
|
96
|
+
}
|
|
97
|
+
else if (entry.isFile() && entry.name === "package.json") {
|
|
98
|
+
results.push(fullPath);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return results;
|
|
102
|
+
}
|
|
103
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
104
|
+
// Main Function
|
|
105
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
87
106
|
export async function createProject(projectName) {
|
|
88
107
|
console.log();
|
|
89
108
|
console.log(pc.cyan(pc.bold(" NYOWORKS Framework")));
|
|
90
109
|
console.log(pc.dim(" Create a new project"));
|
|
91
110
|
console.log();
|
|
92
|
-
let productType = "custom";
|
|
93
|
-
let requiredFeatures = [];
|
|
94
111
|
const nameResponse = await prompts({
|
|
95
112
|
type: projectName ? null : "text",
|
|
96
113
|
name: "name",
|
|
@@ -102,77 +119,119 @@ export async function createProject(projectName) {
|
|
|
102
119
|
console.log(pc.red("Aborted."));
|
|
103
120
|
process.exit(1);
|
|
104
121
|
}
|
|
105
|
-
const productResponse = await prompts({
|
|
106
|
-
type: "select",
|
|
107
|
-
name: "productType",
|
|
108
|
-
message: "Product type:",
|
|
109
|
-
choices: PRODUCT_TYPES,
|
|
110
|
-
initial: 0,
|
|
111
|
-
});
|
|
112
|
-
if (productResponse.productType) {
|
|
113
|
-
productType = productResponse.productType;
|
|
114
|
-
requiredFeatures = REQUIRED_FEATURES[productType] || [];
|
|
115
|
-
}
|
|
116
|
-
const featureChoices = AVAILABLE_FEATURES.map((f) => {
|
|
117
|
-
const isRequired = requiredFeatures.includes(f.value);
|
|
118
|
-
return {
|
|
119
|
-
title: isRequired ? `${f.title} [required]` : f.title,
|
|
120
|
-
value: f.value,
|
|
121
|
-
description: f.description,
|
|
122
|
-
selected: isRequired,
|
|
123
|
-
};
|
|
124
|
-
});
|
|
125
|
-
const response = await prompts([
|
|
126
|
-
{
|
|
127
|
-
type: "multiselect",
|
|
128
|
-
name: "platforms",
|
|
129
|
-
message: "Select platforms:",
|
|
130
|
-
choices: AVAILABLE_PLATFORMS,
|
|
131
|
-
min: 1,
|
|
132
|
-
hint: "- Space to select. Return to submit",
|
|
133
|
-
instructions: false,
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
type: "multiselect",
|
|
137
|
-
name: "features",
|
|
138
|
-
message: "Select features:",
|
|
139
|
-
choices: featureChoices,
|
|
140
|
-
hint: "- Space to select. Return to submit",
|
|
141
|
-
instructions: false,
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
type: "select",
|
|
145
|
-
name: "language",
|
|
146
|
-
message: "Agent response language:",
|
|
147
|
-
choices: AVAILABLE_LANGUAGES,
|
|
148
|
-
initial: 0,
|
|
149
|
-
},
|
|
150
|
-
]);
|
|
151
122
|
const name = (nameResponse.name || projectName);
|
|
152
123
|
const code = generateCode(name);
|
|
153
124
|
const slug = generateSlug(name);
|
|
154
125
|
const databaseName = generateDatabaseName(name);
|
|
155
|
-
const platforms = response.platforms || ["web"];
|
|
156
|
-
const selectedFeatures = response.features || [];
|
|
157
|
-
const features = [...new Set([...requiredFeatures, ...selectedFeatures])];
|
|
158
|
-
const language = response.language || "tr";
|
|
159
126
|
const targetDir = path.resolve(process.cwd(), slug);
|
|
160
127
|
if (fs.existsSync(targetDir)) {
|
|
161
128
|
console.log(pc.red(`Directory ${slug} already exists.`));
|
|
162
129
|
process.exit(1);
|
|
163
130
|
}
|
|
164
131
|
console.log();
|
|
165
|
-
process.stdout.write(pc.cyan("Downloading from GitHub..."));
|
|
166
132
|
let repoDir;
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
133
|
+
let config;
|
|
134
|
+
let isLocalMode = false;
|
|
135
|
+
const localRepo = getLocalRepoPath();
|
|
136
|
+
if (localRepo) {
|
|
137
|
+
process.stdout.write(pc.cyan("Using local framework..."));
|
|
138
|
+
repoDir = localRepo;
|
|
139
|
+
isLocalMode = true;
|
|
140
|
+
try {
|
|
141
|
+
config = await loadAppsConfig(repoDir);
|
|
142
|
+
console.log(pc.green(" done"));
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
console.log(pc.red(" failed"));
|
|
146
|
+
console.error(pc.red("Failed to load local config."));
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
170
149
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
150
|
+
else {
|
|
151
|
+
process.stdout.write(pc.cyan("Downloading from GitHub..."));
|
|
152
|
+
try {
|
|
153
|
+
repoDir = await downloadRepo(REPO, BRANCH);
|
|
154
|
+
config = await loadAppsConfig(repoDir);
|
|
155
|
+
console.log(pc.green(" done"));
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
console.log(pc.red(" failed"));
|
|
159
|
+
console.error(pc.red("Failed to download from GitHub. Check your internet connection."));
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const appChoices = config.apps.map((app) => appToPromptChoice(app, "tr"));
|
|
164
|
+
const appResponse = await prompts({
|
|
165
|
+
type: "select",
|
|
166
|
+
name: "appId",
|
|
167
|
+
message: "App tipi:",
|
|
168
|
+
choices: appChoices,
|
|
169
|
+
initial: 0,
|
|
170
|
+
});
|
|
171
|
+
if (!appResponse.appId) {
|
|
172
|
+
console.log(pc.red("Aborted."));
|
|
174
173
|
process.exit(1);
|
|
175
174
|
}
|
|
175
|
+
const selectedApp = config.apps.find((a) => a.id === appResponse.appId);
|
|
176
|
+
const appPlatforms = getAppPlatforms(selectedApp);
|
|
177
|
+
const appFeatures = getAppFeatures(selectedApp);
|
|
178
|
+
const platformChoices = AVAILABLE_PLATFORMS
|
|
179
|
+
.filter((p) => appPlatforms.includes(p.value))
|
|
180
|
+
.map((p) => ({ ...p, selected: true }));
|
|
181
|
+
const platformResponse = await prompts({
|
|
182
|
+
type: "multiselect",
|
|
183
|
+
name: "platforms",
|
|
184
|
+
message: "Platforms:",
|
|
185
|
+
choices: platformChoices,
|
|
186
|
+
min: 1,
|
|
187
|
+
hint: "- Space to select. Return to submit",
|
|
188
|
+
instructions: false,
|
|
189
|
+
});
|
|
190
|
+
const platforms = platformResponse.platforms || ["web"];
|
|
191
|
+
const selectedProviders = {};
|
|
192
|
+
for (const integrationId of selectedApp.integrations || []) {
|
|
193
|
+
const integration = config.integrations?.[integrationId];
|
|
194
|
+
if (!integration)
|
|
195
|
+
continue;
|
|
196
|
+
const promptConfig = integrationToPromptChoices(integration, integrationId);
|
|
197
|
+
if (!promptConfig) {
|
|
198
|
+
selectedProviders[integrationId] = integration.default || integration.providers?.[0] || integrationId;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
const providerResponse = await prompts({
|
|
202
|
+
type: "select",
|
|
203
|
+
name: "provider",
|
|
204
|
+
message: promptConfig.message,
|
|
205
|
+
choices: promptConfig.choices,
|
|
206
|
+
initial: promptConfig.initial,
|
|
207
|
+
});
|
|
208
|
+
if (providerResponse.provider) {
|
|
209
|
+
selectedProviders[integrationId] = providerResponse.provider;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const additionalFeaturesNotInApp = ADDITIONAL_FEATURES.filter((f) => !appFeatures.includes(f.value));
|
|
213
|
+
let additionalFeatures = [];
|
|
214
|
+
if (additionalFeaturesNotInApp.length > 0) {
|
|
215
|
+
const additionalResponse = await prompts({
|
|
216
|
+
type: "multiselect",
|
|
217
|
+
name: "features",
|
|
218
|
+
message: "Ek feature'lar (isteğe bağlı):",
|
|
219
|
+
choices: additionalFeaturesNotInApp,
|
|
220
|
+
hint: "- Space to select. Return to submit",
|
|
221
|
+
instructions: false,
|
|
222
|
+
});
|
|
223
|
+
additionalFeatures = additionalResponse.features || [];
|
|
224
|
+
}
|
|
225
|
+
const languageResponse = await prompts({
|
|
226
|
+
type: "select",
|
|
227
|
+
name: "language",
|
|
228
|
+
message: "Agent language:",
|
|
229
|
+
choices: AVAILABLE_LANGUAGES,
|
|
230
|
+
initial: 0,
|
|
231
|
+
});
|
|
232
|
+
const language = languageResponse.language || "tr";
|
|
233
|
+
const features = [...new Set([...appFeatures, ...additionalFeatures])];
|
|
234
|
+
const appId = selectedApp.id;
|
|
176
235
|
process.stdout.write(pc.dim(" Copying files..."));
|
|
177
236
|
await fs.ensureDir(targetDir);
|
|
178
237
|
const corePaths = [
|
|
@@ -187,6 +246,7 @@ export async function createProject(projectName) {
|
|
|
187
246
|
"docs/bible",
|
|
188
247
|
"mcp-server",
|
|
189
248
|
".claude",
|
|
249
|
+
"config",
|
|
190
250
|
];
|
|
191
251
|
for (const p of corePaths) {
|
|
192
252
|
const src = path.join(repoDir, p);
|
|
@@ -196,10 +256,20 @@ export async function createProject(projectName) {
|
|
|
196
256
|
}
|
|
197
257
|
}
|
|
198
258
|
for (const platform of platforms) {
|
|
199
|
-
const
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
259
|
+
const templateSrc = path.join(repoDir, `apps/_templates/${platform}`);
|
|
260
|
+
const legacySrc = path.join(repoDir, `apps/${platform}`);
|
|
261
|
+
const dest = path.join(targetDir, `apps/${appId}/${platform}`);
|
|
262
|
+
if (await fs.pathExists(templateSrc)) {
|
|
263
|
+
await fs.copy(templateSrc, dest);
|
|
264
|
+
}
|
|
265
|
+
else if (await fs.pathExists(legacySrc)) {
|
|
266
|
+
await fs.copy(legacySrc, dest);
|
|
267
|
+
}
|
|
268
|
+
const pkgPath = path.join(dest, "package.json");
|
|
269
|
+
if (await fs.pathExists(pkgPath)) {
|
|
270
|
+
const pkg = await fs.readJson(pkgPath);
|
|
271
|
+
pkg.name = `@${slug}/${appId}-${platform}`;
|
|
272
|
+
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
203
273
|
}
|
|
204
274
|
}
|
|
205
275
|
for (const feature of features) {
|
|
@@ -209,6 +279,26 @@ export async function createProject(projectName) {
|
|
|
209
279
|
await fs.copy(src, dest);
|
|
210
280
|
}
|
|
211
281
|
}
|
|
282
|
+
const integrationFeatureMap = {
|
|
283
|
+
payments: ["payments"],
|
|
284
|
+
shipping: ["shipping"],
|
|
285
|
+
invoicing: ["invoicing"],
|
|
286
|
+
whatsapp: ["whatsapp"],
|
|
287
|
+
google: ["google"],
|
|
288
|
+
ai: ["ai"],
|
|
289
|
+
email: [],
|
|
290
|
+
sms: [],
|
|
291
|
+
};
|
|
292
|
+
for (const integrationId of selectedApp.integrations || []) {
|
|
293
|
+
const featuresToCopy = integrationFeatureMap[integrationId] || [];
|
|
294
|
+
for (const featureName of featuresToCopy) {
|
|
295
|
+
const src = path.join(repoDir, `packages/features/${featureName}`);
|
|
296
|
+
const dest = path.join(targetDir, `packages/features/${featureName}`);
|
|
297
|
+
if (await fs.pathExists(src) && !await fs.pathExists(dest)) {
|
|
298
|
+
await fs.copy(src, dest);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
212
302
|
const rootFiles = [
|
|
213
303
|
"package.json",
|
|
214
304
|
"pnpm-workspace.yaml",
|
|
@@ -227,14 +317,21 @@ export async function createProject(projectName) {
|
|
|
227
317
|
await fs.copy(src, dest);
|
|
228
318
|
}
|
|
229
319
|
}
|
|
230
|
-
|
|
320
|
+
if (!isLocalMode) {
|
|
321
|
+
await fs.remove(path.dirname(repoDir));
|
|
322
|
+
}
|
|
323
|
+
console.log(pc.green(" done"));
|
|
324
|
+
process.stdout.write(pc.dim(" Updating package names..."));
|
|
325
|
+
await replacePackageNames(targetDir, slug);
|
|
231
326
|
console.log(pc.green(" done"));
|
|
232
327
|
const placeholders = {
|
|
233
328
|
"${PROJECT_NAME}": name,
|
|
234
329
|
"${PROJECT_CODE}": code,
|
|
235
330
|
"${PROJECT_SLUG}": slug,
|
|
236
331
|
"${DATABASE_NAME}": databaseName,
|
|
237
|
-
"${
|
|
332
|
+
"${APP_ID}": appId,
|
|
333
|
+
"${APP_NAME}": selectedApp.name,
|
|
334
|
+
"${PRODUCT_TYPE}": appId,
|
|
238
335
|
"${RESPONSE_LANGUAGE}": LANGUAGE_RESPONSES[language] || "Turkish",
|
|
239
336
|
};
|
|
240
337
|
process.stdout.write(pc.dim(" Replacing placeholders..."));
|
|
@@ -242,7 +339,8 @@ export async function createProject(projectName) {
|
|
|
242
339
|
console.log(pc.green(" done"));
|
|
243
340
|
for (const feature of features) {
|
|
244
341
|
const featureDoc = path.join(targetDir, "docs", "bible", "features", `${feature}.md`);
|
|
245
|
-
|
|
342
|
+
if (!await fs.pathExists(featureDoc)) {
|
|
343
|
+
const content = `# Feature: ${feature.charAt(0).toUpperCase() + feature.slice(1)}
|
|
246
344
|
|
|
247
345
|
## Overview
|
|
248
346
|
|
|
@@ -275,17 +373,30 @@ See \`docs/bible/data/schema.md\`
|
|
|
275
373
|
|----|----------|-----------|
|
|
276
374
|
| T-xxx | [Decision] | [Why] |
|
|
277
375
|
`;
|
|
278
|
-
|
|
279
|
-
|
|
376
|
+
await fs.outputFile(featureDoc, content);
|
|
377
|
+
console.log(pc.dim(` Created docs/bible/features/${feature}.md`));
|
|
378
|
+
}
|
|
280
379
|
}
|
|
281
380
|
const configPath = path.join(targetDir, "nyoworks.config.yaml");
|
|
282
381
|
if (await fs.pathExists(configPath)) {
|
|
283
|
-
let
|
|
382
|
+
let configContent = await fs.readFile(configPath, "utf8");
|
|
284
383
|
if (features.length > 0) {
|
|
285
|
-
|
|
384
|
+
configContent = configContent.replace(/enabled: \[\]/, `enabled:\n${features.map((f) => ` - ${f}`).join("\n")}`);
|
|
286
385
|
}
|
|
287
|
-
|
|
288
|
-
await fs.writeFile(configPath,
|
|
386
|
+
configContent = configContent.replace(/targets:\n - web/, `targets:\n${platforms.map((p) => ` - ${p}`).join("\n")}`);
|
|
387
|
+
await fs.writeFile(configPath, configContent);
|
|
388
|
+
}
|
|
389
|
+
const workspacePath = path.join(targetDir, "pnpm-workspace.yaml");
|
|
390
|
+
if (await fs.pathExists(workspacePath)) {
|
|
391
|
+
const workspaceContent = `packages:
|
|
392
|
+
- "apps/server"
|
|
393
|
+
- "apps/*/*"
|
|
394
|
+
- "packages/*"
|
|
395
|
+
- "packages/features/*"
|
|
396
|
+
- "packages/platforms/*"
|
|
397
|
+
- "mcp-server"
|
|
398
|
+
`;
|
|
399
|
+
await fs.writeFile(workspacePath, workspaceContent);
|
|
289
400
|
}
|
|
290
401
|
console.log();
|
|
291
402
|
console.log(pc.green(pc.bold("Project created successfully!")));
|
|
@@ -307,9 +418,12 @@ See \`docs/bible/data/schema.md\`
|
|
|
307
418
|
console.log(pc.dim(" Configuration:"));
|
|
308
419
|
console.log(pc.dim(` Name: ${name}`));
|
|
309
420
|
console.log(pc.dim(` Code: ${code}`));
|
|
310
|
-
console.log(pc.dim(`
|
|
421
|
+
console.log(pc.dim(` App: ${selectedApp.name} (${appId})`));
|
|
311
422
|
console.log(pc.dim(` Platforms: ${platforms.join(", ")}`));
|
|
312
423
|
console.log(pc.dim(` Features: ${features.join(", ") || "none"}`));
|
|
424
|
+
if (Object.keys(selectedProviders).length > 0) {
|
|
425
|
+
console.log(pc.dim(` Providers: ${Object.entries(selectedProviders).map(([k, v]) => `${k}:${v}`).join(", ")}`));
|
|
426
|
+
}
|
|
313
427
|
console.log(pc.dim(` Language: ${LANGUAGE_RESPONSES[language] || "Turkish"}`));
|
|
314
428
|
console.log();
|
|
315
429
|
process.stdout.write(pc.dim(" Building MCP server..."));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-nyoworks",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Create a new NYOWORKS project",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"execa": "^9.6.1",
|
|
31
31
|
"fs-extra": "^11.3.3",
|
|
32
32
|
"picocolors": "^1.1.1",
|
|
33
|
-
"prompts": "^2.4.2"
|
|
33
|
+
"prompts": "^2.4.2",
|
|
34
|
+
"yaml": "^2.8.2"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@types/fs-extra": "^11.0.4",
|