gmoonc 0.0.7 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -46,6 +46,16 @@ Your dashboard is now available at:
46
46
 
47
47
  The dashboard code is in `src/gmoonc/` and is independent. You can remove `gmoonc` from `package.json` if desired.
48
48
 
49
+ ## Changelog
50
+
51
+ ### 0.0.9
52
+ - Fix: ensure BrowserRouter import when patched into App.tsx
53
+
54
+ ### 0.0.8
55
+ - Fix: BIN corrected to use CommonJS (.cjs) for Windows compatibility
56
+ - Fix: Router patch now ensures BrowserRouter import is always present
57
+ - Fix: Route order corrected (NotFound "*" route is always last)
58
+
49
59
  ## Uninstalling
50
60
 
51
61
  To remove gmoonc:
@@ -454,30 +454,42 @@ function generateAppRoutes(consumerDir, basePath, routes, appPath, dryRun) {
454
454
  return { success: true };
455
455
  }
456
456
  ensureDirectoryExists(appRoutesPath);
457
- const routeElements = [];
458
- for (const route of routes) {
459
- if (route.path === "/" || route.path === "*") {
460
- routeElements.push(` { path: "${route.path}", element: <${route.componentName} /> }`);
461
- }
462
- }
463
- routeElements.push(` ...createGmooncRoutes({ basePath })`);
457
+ const indexRoute = routes.find((r) => r.path === "/");
458
+ const notFoundRoute = routes.find((r) => r.path === "*");
464
459
  const imports = [
465
460
  "import { useRoutes, type RouteObject } from 'react-router-dom';",
466
461
  "import { createGmooncRoutes } from './createGmooncRoutes';"
467
462
  ];
468
- for (const route of routes) {
469
- if ((route.path === "/" || route.path === "*") && route.import) {
470
- const convertedImport = convertImportToRelative(
471
- route.import.line,
472
- route.import.from,
473
- appRoutesDir,
474
- appDir
475
- );
476
- if (!imports.includes(convertedImport)) {
477
- imports.push(convertedImport);
478
- }
463
+ if (indexRoute && indexRoute.import) {
464
+ const convertedImport = convertImportToRelative(
465
+ indexRoute.import.line,
466
+ indexRoute.import.from,
467
+ appRoutesDir,
468
+ appDir
469
+ );
470
+ if (!imports.includes(convertedImport)) {
471
+ imports.push(convertedImport);
472
+ }
473
+ }
474
+ if (notFoundRoute && notFoundRoute.import) {
475
+ const convertedImport = convertImportToRelative(
476
+ notFoundRoute.import.line,
477
+ notFoundRoute.import.from,
478
+ appRoutesDir,
479
+ appDir
480
+ );
481
+ if (!imports.includes(convertedImport)) {
482
+ imports.push(convertedImport);
479
483
  }
480
484
  }
485
+ const routeElements = [];
486
+ if (indexRoute) {
487
+ routeElements.push(` { path: "${indexRoute.path}", element: <${indexRoute.componentName} /> }`);
488
+ }
489
+ routeElements.push(` ...createGmooncRoutes({ basePath })`);
490
+ if (notFoundRoute) {
491
+ routeElements.push(` { path: "${notFoundRoute.path}", element: <${notFoundRoute.componentName} /> }`);
492
+ }
481
493
  const content = `${imports.join("\n")}
482
494
 
483
495
  export function GMooncAppRoutes({ basePath = "${basePath}" }: { basePath?: string }) {
@@ -552,32 +564,50 @@ function patchBrowserRouter(consumerDir, basePath, dryRun) {
552
564
  const lines = appContent.split("\n");
553
565
  let hasBrowserRouterImport = false;
554
566
  let reactRouterImportIndex = -1;
567
+ let reactRouterImportLine = null;
568
+ let quoteStyle = '"';
555
569
  for (let i = 0; i < lines.length; i++) {
556
- const line = lines[i].trim();
557
- if (line.includes("from") && line.includes("react-router-dom")) {
570
+ const line = lines[i];
571
+ const trimmed = line.trim();
572
+ if (trimmed.startsWith("import ") && (trimmed.includes("'") || trimmed.includes('"'))) {
573
+ if (trimmed.includes("'")) quoteStyle = "'";
574
+ else if (trimmed.includes('"')) quoteStyle = '"';
575
+ }
576
+ if (trimmed.includes("from") && trimmed.includes("react-router-dom")) {
558
577
  reactRouterImportIndex = i;
559
- if (line.includes("BrowserRouter")) {
578
+ reactRouterImportLine = line;
579
+ const browserRouterPattern = /\bBrowserRouter\b/;
580
+ if (browserRouterPattern.test(trimmed)) {
560
581
  hasBrowserRouterImport = true;
561
582
  }
562
583
  }
563
584
  }
564
585
  if (!hasBrowserRouterImport) {
565
- if (reactRouterImportIndex >= 0) {
566
- const existingLine = lines[reactRouterImportIndex];
567
- if (existingLine.includes("{") && existingLine.includes("}")) {
568
- const updatedLine = existingLine.replace(/\{([^}]+)\}/, (match, imports2) => {
569
- const importList = imports2.split(",").map((s) => s.trim());
570
- if (!importList.includes("BrowserRouter")) {
571
- importList.push("BrowserRouter");
572
- }
573
- return `{ ${importList.join(", ")} }`;
574
- });
575
- lines[reactRouterImportIndex] = updatedLine;
586
+ if (reactRouterImportIndex >= 0 && reactRouterImportLine) {
587
+ const existingLine = reactRouterImportLine;
588
+ const indent2 = existingLine.match(/^(\s*)/)?.[1] || "";
589
+ const namedMatch = existingLine.match(/^(\s*)import\s+\{([^}]+)\}\s+from\s+(["'])react-router-dom\2/);
590
+ if (namedMatch) {
591
+ const importListStr = namedMatch[2];
592
+ const quote = namedMatch[3];
593
+ const importItems = importListStr.split(",").map((s) => s.trim()).filter(Boolean);
594
+ const hasBrowserRouter = importItems.some(
595
+ (item) => item === "BrowserRouter" || item === "type BrowserRouter" || item.includes("BrowserRouter")
596
+ );
597
+ if (!hasBrowserRouter) {
598
+ importItems.push("BrowserRouter");
599
+ const hasTrailingComma = importListStr.trim().endsWith(",");
600
+ const separator = importListStr.includes(",\n") ? ",\n" : ", ";
601
+ const formatted = importItems.join(separator) + (hasTrailingComma ? "," : "");
602
+ const updatedLine = `${indent2}import { ${formatted} } from ${quote}react-router-dom${quote};`;
603
+ lines[reactRouterImportIndex] = updatedLine;
604
+ }
576
605
  } else {
577
- lines.splice(reactRouterImportIndex + 1, 0, `import { BrowserRouter } from "react-router-dom";`);
606
+ const newImportLine = `${indent2}import { BrowserRouter } from ${quoteStyle}react-router-dom${quoteStyle};`;
607
+ lines.splice(reactRouterImportIndex + 1, 0, newImportLine);
578
608
  }
579
609
  } else {
580
- const importLine2 = `import { BrowserRouter } from "react-router-dom";`;
610
+ const importLine2 = `import { BrowserRouter } from ${quoteStyle}react-router-dom${quoteStyle};`;
581
611
  let lastImportIndex2 = -1;
582
612
  for (let i = 0; i < lines.length; i++) {
583
613
  const line = lines[i].trim();
@@ -659,7 +689,7 @@ function patchBrowserRouter(consumerDir, basePath, dryRun) {
659
689
 
660
690
  // src/cli/index.ts
661
691
  var program = new import_commander.Command();
662
- program.name("gmoonc").description("Goalmoon Ctrl (gmoonc): Install complete dashboard into your React project").version("0.0.7").option("--base <path>", "Base path for dashboard routes", "/app").option("--skip-router-patch", "Skip automatic router integration (only copy files and inject CSS)").option("--dry-run", "Show what would be done without making changes").action(async (options) => {
692
+ program.name("gmoonc").description("Goalmoon Ctrl (gmoonc): Install complete dashboard into your React project").version("0.0.9").option("--base <path>", "Base path for dashboard routes", "/app").option("--skip-router-patch", "Skip automatic router integration (only copy files and inject CSS)").option("--dry-run", "Show what would be done without making changes").action(async (options) => {
663
693
  try {
664
694
  logInfo("\u{1F680} Starting gmoonc installer...");
665
695
  logInfo("\u{1F4E6} Installing complete dashboard into your React project\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gmoonc",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "Goalmoon Ctrl (gmoonc): Complete dashboard installer for React projects",
5
5
  "license": "MIT",
6
6
  "homepage": "https://gmoonc.com",
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "type": "module",
16
16
  "bin": {
17
- "gmoonc": "./dist/index.js"
17
+ "gmoonc": "dist/index.cjs"
18
18
  },
19
19
  "files": [
20
20
  "dist",
@@ -25,7 +25,7 @@
25
25
  ],
26
26
  "scripts": {
27
27
  "sync:templates": "node scripts/sync-templates.mjs",
28
- "build": "tsup src/cli/index.ts --format cjs --no-splitting --out-dir dist && node -e \"const fs=require('fs');const path=require('path');const distDir='dist';const files=fs.readdirSync(distDir);const indexFile=files.find(f=>(f.startsWith('index')&&(f.endsWith('.js')||f.endsWith('.cjs'))));if(indexFile){const oldPath=path.join(distDir,indexFile);const newPath=path.join(distDir,'index.js');if(oldPath!==newPath){fs.renameSync(oldPath,newPath);}const content=fs.readFileSync(newPath,'utf8');if(!content.startsWith('#!/usr/bin/env node')){fs.writeFileSync(newPath,'#!/usr/bin/env node\\n'+content)}}\"",
28
+ "build": "tsup src/cli/index.ts --format cjs --no-splitting --out-dir dist && node -e \"const fs=require('fs');const path=require('path');const distDir='dist';const files=fs.readdirSync(distDir);const indexFile=files.find(f=>(f.startsWith('index')&&(f.endsWith('.js')||f.endsWith('.cjs'))));if(indexFile){const oldPath=path.join(distDir,indexFile);const newPath=path.join(distDir,'index.cjs');if(oldPath!==newPath){fs.renameSync(oldPath,newPath);}const content=fs.readFileSync(newPath,'utf8');if(!content.startsWith('#!/usr/bin/env node')){fs.writeFileSync(newPath,'#!/usr/bin/env node\\n'+content)}}\"",
29
29
  "clean": "rimraf dist",
30
30
  "prepublishOnly": "npm run clean && npm run build",
31
31
  "postinstall": "node scripts/block-global-install.cjs"