create-nyoworks 3.0.0 → 3.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.
Files changed (2) hide show
  1. package/dist/init.js +207 -54
  2. package/package.json +1 -1
package/dist/init.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // ═══════════════════════════════════════════════════════════════════════════════
2
- // Create NYOWORKS - Project Initialization
2
+ // Create NYOWORKS - Project Initialization (Multi-Product Support)
3
3
  // ═══════════════════════════════════════════════════════════════════════════════
4
4
  import prompts from "prompts";
5
5
  import pc from "picocolors";
@@ -106,7 +106,7 @@ async function findPackageJsonFiles(dir) {
106
106
  export async function createProject(projectName) {
107
107
  console.log();
108
108
  console.log(pc.cyan(pc.bold(" NYOWORKS Framework")));
109
- console.log(pc.dim(" Create a new project"));
109
+ console.log(pc.dim(" Create a new multi-product project"));
110
110
  console.log();
111
111
  const nameResponse = await prompts({
112
112
  type: projectName ? null : "text",
@@ -160,36 +160,68 @@ export async function createProject(projectName) {
160
160
  process.exit(1);
161
161
  }
162
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."));
173
- process.exit(1);
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({
163
+ // ─────────────────────────────────────────────────────────────────────────────
164
+ // Multi-Product Selection
165
+ // ─────────────────────────────────────────────────────────────────────────────
166
+ const appChoices = config.apps.map((app) => ({
167
+ ...appToPromptChoice(app, "tr"),
168
+ selected: false,
169
+ }));
170
+ const productsResponse = await prompts({
182
171
  type: "multiselect",
183
- name: "platforms",
184
- message: "Platforms:",
185
- choices: platformChoices,
172
+ name: "appIds",
173
+ message: "Products (birden fazla seçebilirsiniz):",
174
+ choices: appChoices,
186
175
  min: 1,
187
176
  hint: "- Space to select. Return to submit",
188
177
  instructions: false,
189
178
  });
190
- const platforms = platformResponse.platforms || ["web"];
179
+ if (!productsResponse.appIds || productsResponse.appIds.length === 0) {
180
+ console.log(pc.red("Aborted."));
181
+ process.exit(1);
182
+ }
183
+ const selectedAppIds = productsResponse.appIds;
184
+ const selectedApps = config.apps.filter((a) => selectedAppIds.includes(a.id));
185
+ // ─────────────────────────────────────────────────────────────────────────────
186
+ // Per-Product Platform Selection
187
+ // ─────────────────────────────────────────────────────────────────────────────
188
+ const productSelections = [];
189
+ for (const app of selectedApps) {
190
+ console.log();
191
+ console.log(pc.cyan(` ${app.name_tr || app.name} platformları:`));
192
+ const appPlatforms = getAppPlatforms(app);
193
+ const platformChoices = AVAILABLE_PLATFORMS
194
+ .filter((p) => appPlatforms.includes(p.value))
195
+ .map((p) => ({ ...p, selected: p.value === "web" }));
196
+ const platformResponse = await prompts({
197
+ type: "multiselect",
198
+ name: "platforms",
199
+ message: `${app.id} platforms:`,
200
+ choices: platformChoices,
201
+ min: 1,
202
+ hint: "- Space to select. Return to submit",
203
+ instructions: false,
204
+ });
205
+ const platforms = (platformResponse.platforms || ["web"]);
206
+ productSelections.push({ app, platforms });
207
+ }
208
+ // ─────────────────────────────────────────────────────────────────────────────
209
+ // Collect All Features from All Products
210
+ // ─────────────────────────────────────────────────────────────────────────────
211
+ const allAppFeatures = new Set();
212
+ const allIntegrations = new Set();
213
+ for (const selection of productSelections) {
214
+ const features = getAppFeatures(selection.app);
215
+ features.forEach((f) => allAppFeatures.add(f));
216
+ for (const integration of selection.app.integrations || []) {
217
+ allIntegrations.add(integration);
218
+ }
219
+ }
220
+ // ─────────────────────────────────────────────────────────────────────────────
221
+ // Integration Provider Selection (Once for All Products)
222
+ // ─────────────────────────────────────────────────────────────────────────────
191
223
  const selectedProviders = {};
192
- for (const integrationId of selectedApp.integrations || []) {
224
+ for (const integrationId of allIntegrations) {
193
225
  const integration = config.integrations?.[integrationId];
194
226
  if (!integration)
195
227
  continue;
@@ -209,19 +241,25 @@ export async function createProject(projectName) {
209
241
  selectedProviders[integrationId] = providerResponse.provider;
210
242
  }
211
243
  }
212
- const additionalFeaturesNotInApp = ADDITIONAL_FEATURES.filter((f) => !appFeatures.includes(f.value));
244
+ // ─────────────────────────────────────────────────────────────────────────────
245
+ // Additional Features
246
+ // ─────────────────────────────────────────────────────────────────────────────
247
+ const additionalFeaturesNotInApps = ADDITIONAL_FEATURES.filter((f) => !allAppFeatures.has(f.value));
213
248
  let additionalFeatures = [];
214
- if (additionalFeaturesNotInApp.length > 0) {
249
+ if (additionalFeaturesNotInApps.length > 0) {
215
250
  const additionalResponse = await prompts({
216
251
  type: "multiselect",
217
252
  name: "features",
218
253
  message: "Ek feature'lar (isteğe bağlı):",
219
- choices: additionalFeaturesNotInApp,
254
+ choices: additionalFeaturesNotInApps,
220
255
  hint: "- Space to select. Return to submit",
221
256
  instructions: false,
222
257
  });
223
258
  additionalFeatures = additionalResponse.features || [];
224
259
  }
260
+ // ─────────────────────────────────────────────────────────────────────────────
261
+ // Language Selection
262
+ // ─────────────────────────────────────────────────────────────────────────────
225
263
  const languageResponse = await prompts({
226
264
  type: "select",
227
265
  name: "language",
@@ -230,10 +268,26 @@ export async function createProject(projectName) {
230
268
  initial: 0,
231
269
  });
232
270
  const language = languageResponse.language || "tr";
233
- const features = [...new Set([...appFeatures, ...additionalFeatures])];
234
- const appId = selectedApp.id;
271
+ const features = [...new Set([...allAppFeatures, ...additionalFeatures])];
272
+ // ─────────────────────────────────────────────────────────────────────────────
273
+ // Build Project Config
274
+ // ─────────────────────────────────────────────────────────────────────────────
275
+ const projectConfig = {
276
+ name,
277
+ code,
278
+ slug,
279
+ databaseName,
280
+ products: productSelections,
281
+ features,
282
+ providers: selectedProviders,
283
+ language,
284
+ };
285
+ console.log();
235
286
  process.stdout.write(pc.dim(" Copying files..."));
236
287
  await fs.ensureDir(targetDir);
288
+ // ─────────────────────────────────────────────────────────────────────────────
289
+ // Copy Core Packages (Shared)
290
+ // ─────────────────────────────────────────────────────────────────────────────
237
291
  const corePaths = [
238
292
  "packages/api",
239
293
  "packages/api-client",
@@ -255,23 +309,32 @@ export async function createProject(projectName) {
255
309
  await fs.copy(src, dest);
256
310
  }
257
311
  }
258
- for (const platform of platforms) {
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 });
312
+ // ─────────────────────────────────────────────────────────────────────────────
313
+ // Copy Apps (Per Product + Platform)
314
+ // ─────────────────────────────────────────────────────────────────────────────
315
+ for (const selection of productSelections) {
316
+ const appId = selection.app.id;
317
+ for (const platform of selection.platforms) {
318
+ const templateSrc = path.join(repoDir, `apps/_templates/${platform}`);
319
+ const legacySrc = path.join(repoDir, `apps/${platform}`);
320
+ const dest = path.join(targetDir, `apps/${appId}/${platform}`);
321
+ if (await fs.pathExists(templateSrc)) {
322
+ await fs.copy(templateSrc, dest);
323
+ }
324
+ else if (await fs.pathExists(legacySrc)) {
325
+ await fs.copy(legacySrc, dest);
326
+ }
327
+ const pkgPath = path.join(dest, "package.json");
328
+ if (await fs.pathExists(pkgPath)) {
329
+ const pkg = await fs.readJson(pkgPath);
330
+ pkg.name = `@${slug}/${appId}-${platform}`;
331
+ await fs.writeJson(pkgPath, pkg, { spaces: 2 });
332
+ }
273
333
  }
274
334
  }
335
+ // ─────────────────────────────────────────────────────────────────────────────
336
+ // Copy Feature Packages
337
+ // ─────────────────────────────────────────────────────────────────────────────
275
338
  for (const feature of features) {
276
339
  const src = path.join(repoDir, `packages/features/${feature}`);
277
340
  const dest = path.join(targetDir, `packages/features/${feature}`);
@@ -289,7 +352,7 @@ export async function createProject(projectName) {
289
352
  email: [],
290
353
  sms: [],
291
354
  };
292
- for (const integrationId of selectedApp.integrations || []) {
355
+ for (const integrationId of allIntegrations) {
293
356
  const featuresToCopy = integrationFeatureMap[integrationId] || [];
294
357
  for (const featureName of featuresToCopy) {
295
358
  const src = path.join(repoDir, `packages/features/${featureName}`);
@@ -299,6 +362,9 @@ export async function createProject(projectName) {
299
362
  }
300
363
  }
301
364
  }
365
+ // ─────────────────────────────────────────────────────────────────────────────
366
+ // Copy Root Files
367
+ // ─────────────────────────────────────────────────────────────────────────────
302
368
  const rootFiles = [
303
369
  "package.json",
304
370
  "pnpm-workspace.yaml",
@@ -321,22 +387,33 @@ export async function createProject(projectName) {
321
387
  await fs.remove(path.dirname(repoDir));
322
388
  }
323
389
  console.log(pc.green(" done"));
390
+ // ─────────────────────────────────────────────────────────────────────────────
391
+ // Update Package Names
392
+ // ─────────────────────────────────────────────────────────────────────────────
324
393
  process.stdout.write(pc.dim(" Updating package names..."));
325
394
  await replacePackageNames(targetDir, slug);
326
395
  console.log(pc.green(" done"));
396
+ // ─────────────────────────────────────────────────────────────────────────────
397
+ // Replace Placeholders
398
+ // ─────────────────────────────────────────────────────────────────────────────
399
+ const primaryApp = productSelections[0].app;
400
+ const allPlatforms = [...new Set(productSelections.flatMap((s) => s.platforms))];
327
401
  const placeholders = {
328
402
  "${PROJECT_NAME}": name,
329
403
  "${PROJECT_CODE}": code,
330
404
  "${PROJECT_SLUG}": slug,
331
405
  "${DATABASE_NAME}": databaseName,
332
- "${APP_ID}": appId,
333
- "${APP_NAME}": selectedApp.name,
334
- "${PRODUCT_TYPE}": appId,
406
+ "${APP_ID}": primaryApp.id,
407
+ "${APP_NAME}": primaryApp.name,
408
+ "${PRODUCT_TYPE}": primaryApp.id,
335
409
  "${RESPONSE_LANGUAGE}": LANGUAGE_RESPONSES[language] || "Turkish",
336
410
  };
337
411
  process.stdout.write(pc.dim(" Replacing placeholders..."));
338
412
  await replacePlaceholders(targetDir, placeholders);
339
413
  console.log(pc.green(" done"));
414
+ // ─────────────────────────────────────────────────────────────────────────────
415
+ // Create Feature Docs
416
+ // ─────────────────────────────────────────────────────────────────────────────
340
417
  for (const feature of features) {
341
418
  const featureDoc = path.join(targetDir, "docs", "bible", "features", `${feature}.md`);
342
419
  if (!await fs.pathExists(featureDoc)) {
@@ -377,15 +454,27 @@ See \`docs/bible/data/schema.md\`
377
454
  console.log(pc.dim(` Created docs/bible/features/${feature}.md`));
378
455
  }
379
456
  }
457
+ // ─────────────────────────────────────────────────────────────────────────────
458
+ // Generate Multi-Product Config (apps.config.yaml)
459
+ // ─────────────────────────────────────────────────────────────────────────────
460
+ const appsConfigContent = generateAppsConfigYaml(projectConfig);
461
+ await fs.outputFile(path.join(targetDir, "config", "apps.config.yaml"), appsConfigContent);
462
+ console.log(pc.dim(" Created config/apps.config.yaml (multi-product config)"));
463
+ // ─────────────────────────────────────────────────────────────────────────────
464
+ // Update nyoworks.config.yaml
465
+ // ─────────────────────────────────────────────────────────────────────────────
380
466
  const configPath = path.join(targetDir, "nyoworks.config.yaml");
381
467
  if (await fs.pathExists(configPath)) {
382
468
  let configContent = await fs.readFile(configPath, "utf8");
383
469
  if (features.length > 0) {
384
470
  configContent = configContent.replace(/enabled: \[\]/, `enabled:\n${features.map((f) => ` - ${f}`).join("\n")}`);
385
471
  }
386
- configContent = configContent.replace(/targets:\n - web/, `targets:\n${platforms.map((p) => ` - ${p}`).join("\n")}`);
472
+ configContent = configContent.replace(/targets:\n - web/, `targets:\n${allPlatforms.map((p) => ` - ${p}`).join("\n")}`);
387
473
  await fs.writeFile(configPath, configContent);
388
474
  }
475
+ // ─────────────────────────────────────────────────────────────────────────────
476
+ // Update pnpm-workspace.yaml
477
+ // ─────────────────────────────────────────────────────────────────────────────
389
478
  const workspacePath = path.join(targetDir, "pnpm-workspace.yaml");
390
479
  if (await fs.pathExists(workspacePath)) {
391
480
  const workspaceContent = `packages:
@@ -398,8 +487,11 @@ See \`docs/bible/data/schema.md\`
398
487
  `;
399
488
  await fs.writeFile(workspacePath, workspaceContent);
400
489
  }
490
+ // ─────────────────────────────────────────────────────────────────────────────
491
+ // Success Message
492
+ // ─────────────────────────────────────────────────────────────────────────────
401
493
  console.log();
402
- console.log(pc.green(pc.bold("Project created successfully!")));
494
+ console.log(pc.green(pc.bold("Multi-product project created successfully!")));
403
495
  await checkDependencies();
404
496
  const dockerCmd = await getDockerComposeCommand();
405
497
  showClaudeMaxWarning();
@@ -418,14 +510,19 @@ See \`docs/bible/data/schema.md\`
418
510
  console.log(pc.dim(" Configuration:"));
419
511
  console.log(pc.dim(` Name: ${name}`));
420
512
  console.log(pc.dim(` Code: ${code}`));
421
- console.log(pc.dim(` App: ${selectedApp.name} (${appId})`));
422
- console.log(pc.dim(` Platforms: ${platforms.join(", ")}`));
513
+ console.log(pc.dim(" Products:"));
514
+ for (const selection of productSelections) {
515
+ console.log(pc.dim(` - ${selection.app.name} (${selection.app.id}): ${selection.platforms.join(", ")}`));
516
+ }
423
517
  console.log(pc.dim(` Features: ${features.join(", ") || "none"}`));
424
518
  if (Object.keys(selectedProviders).length > 0) {
425
519
  console.log(pc.dim(` Providers: ${Object.entries(selectedProviders).map(([k, v]) => `${k}:${v}`).join(", ")}`));
426
520
  }
427
521
  console.log(pc.dim(` Language: ${LANGUAGE_RESPONSES[language] || "Turkish"}`));
428
522
  console.log();
523
+ // ─────────────────────────────────────────────────────────────────────────────
524
+ // Build MCP Server
525
+ // ─────────────────────────────────────────────────────────────────────────────
429
526
  process.stdout.write(pc.dim(" Building MCP server..."));
430
527
  const mcpPath = path.join(targetDir, "mcp-server");
431
528
  try {
@@ -455,3 +552,59 @@ See \`docs/bible/data/schema.md\`
455
552
  await fs.outputJson(mcpConfigPath, mcpConfig, { spaces: 2 });
456
553
  console.log(pc.dim(" Created .claude/settings.local.json"));
457
554
  }
555
+ // ─────────────────────────────────────────────────────────────────────────────
556
+ // Generate Multi-Product Config YAML
557
+ // ─────────────────────────────────────────────────────────────────────────────
558
+ function generateAppsConfigYaml(config) {
559
+ const productsYaml = config.products.map((p) => ` ${p.app.id}:
560
+ name: "${p.app.name}"
561
+ name_tr: "${p.app.name_tr || p.app.name}"
562
+ platforms:
563
+ ${p.platforms.map((pl) => ` - ${pl}`).join("\n")}
564
+ features:
565
+ ${getAppFeatures(p.app).map((f) => ` - ${f}`).join("\n")}`).join("\n\n");
566
+ const providersYaml = Object.entries(config.providers)
567
+ .map(([k, v]) => ` ${k}: "${v}"`)
568
+ .join("\n");
569
+ return `# ═══════════════════════════════════════════════════════════════════════════════
570
+ # ${config.name} - Multi-Product Configuration
571
+ # ═══════════════════════════════════════════════════════════════════════════════
572
+
573
+ project:
574
+ name: "${config.name}"
575
+ code: "${config.code}"
576
+ slug: "${config.slug}"
577
+ database: "${config.databaseName}"
578
+ language: "${config.language}"
579
+
580
+ # ─────────────────────────────────────────────────────────────────────────────
581
+ # Products (Apps)
582
+ # ─────────────────────────────────────────────────────────────────────────────
583
+
584
+ products:
585
+ ${productsYaml}
586
+
587
+ # ─────────────────────────────────────────────────────────────────────────────
588
+ # Shared Features (across all products)
589
+ # ─────────────────────────────────────────────────────────────────────────────
590
+
591
+ shared_features:
592
+ ${config.features.map((f) => ` - ${f}`).join("\n")}
593
+
594
+ # ─────────────────────────────────────────────────────────────────────────────
595
+ # Integration Providers
596
+ # ─────────────────────────────────────────────────────────────────────────────
597
+
598
+ providers:
599
+ ${providersYaml}
600
+
601
+ # ─────────────────────────────────────────────────────────────────────────────
602
+ # Security Configuration
603
+ # ─────────────────────────────────────────────────────────────────────────────
604
+
605
+ security:
606
+ rls_enabled: true
607
+ app_scoped_routes: true
608
+ branded_validators: true
609
+ `;
610
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-nyoworks",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "Create a new NYOWORKS project",
5
5
  "type": "module",
6
6
  "bin": {