@launch77/cli 1.2.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/.eslintrc.json +6 -0
- package/dist/app-templates/webapp/.env.ci +6 -0
- package/dist/app-templates/webapp/.env.example +9 -0
- package/dist/app-templates/webapp/.eslintrc.json +6 -0
- package/dist/app-templates/webapp/README.md.hbs +80 -0
- package/dist/app-templates/webapp/app/about/page.tsx.hbs +41 -0
- package/dist/app-templates/webapp/app/dashboard/page.tsx.hbs +51 -0
- package/dist/app-templates/webapp/app/globals.css +31 -0
- package/dist/app-templates/webapp/app/layout.tsx.hbs +26 -0
- package/dist/app-templates/webapp/app/page.tsx.hbs +30 -0
- package/dist/app-templates/webapp/next.config.js +99 -0
- package/dist/app-templates/webapp/package.json.hbs +30 -0
- package/dist/app-templates/webapp/postcss.config.js +6 -0
- package/dist/app-templates/webapp/tailwind.config.ts +24 -0
- package/dist/app-templates/webapp/tsconfig.json +29 -0
- package/dist/app-templates/webapp/vercel.json.hbs +7 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +29 -0
- package/dist/cli.js.map +1 -0
- package/dist/infrastructure/filesystem.d.ts +41 -0
- package/dist/infrastructure/filesystem.d.ts.map +1 -0
- package/dist/infrastructure/filesystem.js +61 -0
- package/dist/infrastructure/filesystem.js.map +1 -0
- package/dist/infrastructure/npm.d.ts +9 -0
- package/dist/infrastructure/npm.d.ts.map +1 -0
- package/dist/infrastructure/npm.js +17 -0
- package/dist/infrastructure/npm.js.map +1 -0
- package/dist/infrastructure/template-engine.d.ts +12 -0
- package/dist/infrastructure/template-engine.d.ts.map +1 -0
- package/dist/infrastructure/template-engine.js +40 -0
- package/dist/infrastructure/template-engine.js.map +1 -0
- package/dist/infrastructure/template-generator.d.ts +9 -0
- package/dist/infrastructure/template-generator.d.ts.map +1 -0
- package/dist/infrastructure/template-generator.js +19 -0
- package/dist/infrastructure/template-generator.js.map +1 -0
- package/dist/infrastructure/template.d.ts +20 -0
- package/dist/infrastructure/template.d.ts.map +1 -0
- package/dist/infrastructure/template.js +53 -0
- package/dist/infrastructure/template.js.map +1 -0
- package/dist/modules/app/commands/create-app.d.ts +3 -0
- package/dist/modules/app/commands/create-app.d.ts.map +1 -0
- package/dist/modules/app/commands/create-app.js +51 -0
- package/dist/modules/app/commands/create-app.js.map +1 -0
- package/dist/modules/app/commands/delete-app.d.ts +3 -0
- package/dist/modules/app/commands/delete-app.d.ts.map +1 -0
- package/dist/modules/app/commands/delete-app.js +109 -0
- package/dist/modules/app/commands/delete-app.js.map +1 -0
- package/dist/modules/app/commands/generate-manifest.d.ts +3 -0
- package/dist/modules/app/commands/generate-manifest.d.ts.map +1 -0
- package/dist/modules/app/commands/generate-manifest.js +62 -0
- package/dist/modules/app/commands/generate-manifest.js.map +1 -0
- package/dist/modules/app/commands/validate-manifest.d.ts +3 -0
- package/dist/modules/app/commands/validate-manifest.d.ts.map +1 -0
- package/dist/modules/app/commands/validate-manifest.js +68 -0
- package/dist/modules/app/commands/validate-manifest.js.map +1 -0
- package/dist/modules/app/errors/app-errors.d.ts +19 -0
- package/dist/modules/app/errors/app-errors.d.ts.map +1 -0
- package/dist/modules/app/errors/app-errors.js +37 -0
- package/dist/modules/app/errors/app-errors.js.map +1 -0
- package/dist/modules/app/index.d.ts +10 -0
- package/dist/modules/app/index.d.ts.map +1 -0
- package/dist/modules/app/index.js +11 -0
- package/dist/modules/app/index.js.map +1 -0
- package/dist/modules/app/lib/manifest-schema.d.ts +39 -0
- package/dist/modules/app/lib/manifest-schema.d.ts.map +1 -0
- package/dist/modules/app/lib/manifest-schema.js +44 -0
- package/dist/modules/app/lib/manifest-schema.js.map +1 -0
- package/dist/modules/app/services/app-svc.d.ts +15 -0
- package/dist/modules/app/services/app-svc.d.ts.map +1 -0
- package/dist/modules/app/services/app-svc.js +90 -0
- package/dist/modules/app/services/app-svc.js.map +1 -0
- package/dist/modules/app/services/manifest-svc.d.ts +28 -0
- package/dist/modules/app/services/manifest-svc.d.ts.map +1 -0
- package/dist/modules/app/services/manifest-svc.js +119 -0
- package/dist/modules/app/services/manifest-svc.js.map +1 -0
- package/dist/modules/app/types/app-types.d.ts +21 -0
- package/dist/modules/app/types/app-types.d.ts.map +1 -0
- package/dist/modules/app/types/app-types.js +5 -0
- package/dist/modules/app/types/app-types.js.map +1 -0
- package/dist/modules/catalog/commands/generate.d.ts +9 -0
- package/dist/modules/catalog/commands/generate.d.ts.map +1 -0
- package/dist/modules/catalog/commands/generate.js +94 -0
- package/dist/modules/catalog/commands/generate.js.map +1 -0
- package/dist/modules/catalog/commands/scan.d.ts +9 -0
- package/dist/modules/catalog/commands/scan.d.ts.map +1 -0
- package/dist/modules/catalog/commands/scan.js +43 -0
- package/dist/modules/catalog/commands/scan.js.map +1 -0
- package/dist/modules/catalog/config/catalog-config.d.ts +49 -0
- package/dist/modules/catalog/config/catalog-config.d.ts.map +1 -0
- package/dist/modules/catalog/config/catalog-config.js +116 -0
- package/dist/modules/catalog/config/catalog-config.js.map +1 -0
- package/dist/modules/catalog/config/catalog-config.test.d.ts +2 -0
- package/dist/modules/catalog/config/catalog-config.test.d.ts.map +1 -0
- package/dist/modules/catalog/config/catalog-config.test.js +362 -0
- package/dist/modules/catalog/config/catalog-config.test.js.map +1 -0
- package/dist/modules/catalog/index.d.ts +9 -0
- package/dist/modules/catalog/index.d.ts.map +1 -0
- package/dist/modules/catalog/index.js +9 -0
- package/dist/modules/catalog/index.js.map +1 -0
- package/dist/modules/catalog/parsers/typescript-parser.d.ts +15 -0
- package/dist/modules/catalog/parsers/typescript-parser.d.ts.map +1 -0
- package/dist/modules/catalog/parsers/typescript-parser.js +82 -0
- package/dist/modules/catalog/parsers/typescript-parser.js.map +1 -0
- package/dist/modules/catalog/scanners/metadata-extractor.d.ts +10 -0
- package/dist/modules/catalog/scanners/metadata-extractor.d.ts.map +1 -0
- package/dist/modules/catalog/scanners/metadata-extractor.js +122 -0
- package/dist/modules/catalog/scanners/metadata-extractor.js.map +1 -0
- package/dist/modules/catalog/scanners/metadata-validator.d.ts +6 -0
- package/dist/modules/catalog/scanners/metadata-validator.d.ts.map +1 -0
- package/dist/modules/catalog/scanners/metadata-validator.js +57 -0
- package/dist/modules/catalog/scanners/metadata-validator.js.map +1 -0
- package/dist/modules/catalog/scanners/ui-component-scanner.d.ts +11 -0
- package/dist/modules/catalog/scanners/ui-component-scanner.d.ts.map +1 -0
- package/dist/modules/catalog/scanners/ui-component-scanner.js +147 -0
- package/dist/modules/catalog/scanners/ui-component-scanner.js.map +1 -0
- package/dist/modules/catalog/schemas/catalog-ui-components.schema.json +161 -0
- package/dist/modules/catalog/schemas/schemas/catalog-ui-components.schema.json +145 -0
- package/dist/modules/catalog/services/catalog-generator-svc.d.ts +27 -0
- package/dist/modules/catalog/services/catalog-generator-svc.d.ts.map +1 -0
- package/dist/modules/catalog/services/catalog-generator-svc.js +100 -0
- package/dist/modules/catalog/services/catalog-generator-svc.js.map +1 -0
- package/dist/modules/catalog/services/catalog-generator-svc.test.d.ts +2 -0
- package/dist/modules/catalog/services/catalog-generator-svc.test.d.ts.map +1 -0
- package/dist/modules/catalog/services/catalog-generator-svc.test.js +201 -0
- package/dist/modules/catalog/services/catalog-generator-svc.test.js.map +1 -0
- package/dist/modules/catalog/services/catalog-svc.d.ts +39 -0
- package/dist/modules/catalog/services/catalog-svc.d.ts.map +1 -0
- package/dist/modules/catalog/services/catalog-svc.js +163 -0
- package/dist/modules/catalog/services/catalog-svc.js.map +1 -0
- package/dist/modules/catalog/types/catalog-types.d.ts +120 -0
- package/dist/modules/catalog/types/catalog-types.d.ts.map +1 -0
- package/dist/modules/catalog/types/catalog-types.js +5 -0
- package/dist/modules/catalog/types/catalog-types.js.map +1 -0
- package/dist/modules/catalog/utils/examples-generator.d.ts +6 -0
- package/dist/modules/catalog/utils/examples-generator.d.ts.map +1 -0
- package/dist/modules/catalog/utils/examples-generator.js +55 -0
- package/dist/modules/catalog/utils/examples-generator.js.map +1 -0
- package/dist/modules/catalog/utils/quality-reporter.d.ts +13 -0
- package/dist/modules/catalog/utils/quality-reporter.d.ts.map +1 -0
- package/dist/modules/catalog/utils/quality-reporter.js +64 -0
- package/dist/modules/catalog/utils/quality-reporter.js.map +1 -0
- package/dist/modules/catalog/validators/component-name-validator.d.ts +20 -0
- package/dist/modules/catalog/validators/component-name-validator.d.ts.map +1 -0
- package/dist/modules/catalog/validators/component-name-validator.js +33 -0
- package/dist/modules/catalog/validators/component-name-validator.js.map +1 -0
- package/dist/modules/catalog/validators/component-name-validator.test.d.ts +2 -0
- package/dist/modules/catalog/validators/component-name-validator.test.d.ts.map +1 -0
- package/dist/modules/catalog/validators/component-name-validator.test.js +196 -0
- package/dist/modules/catalog/validators/component-name-validator.test.js.map +1 -0
- package/dist/modules/deploy/commands/deploy-init-action.d.ts +12 -0
- package/dist/modules/deploy/commands/deploy-init-action.d.ts.map +1 -0
- package/dist/modules/deploy/commands/deploy-init-action.js +220 -0
- package/dist/modules/deploy/commands/deploy-init-action.js.map +1 -0
- package/dist/modules/deploy/commands/deploy-init.d.ts +6 -0
- package/dist/modules/deploy/commands/deploy-init.d.ts.map +1 -0
- package/dist/modules/deploy/commands/deploy-init.js +16 -0
- package/dist/modules/deploy/commands/deploy-init.js.map +1 -0
- package/dist/modules/deploy/commands/deploy-logs-action.d.ts +12 -0
- package/dist/modules/deploy/commands/deploy-logs-action.d.ts.map +1 -0
- package/dist/modules/deploy/commands/deploy-logs-action.js +113 -0
- package/dist/modules/deploy/commands/deploy-logs-action.js.map +1 -0
- package/dist/modules/deploy/commands/deploy-logs.d.ts +6 -0
- package/dist/modules/deploy/commands/deploy-logs.d.ts.map +1 -0
- package/dist/modules/deploy/commands/deploy-logs.js +17 -0
- package/dist/modules/deploy/commands/deploy-logs.js.map +1 -0
- package/dist/modules/deploy/commands/deploy-status-action.d.ts +8 -0
- package/dist/modules/deploy/commands/deploy-status-action.d.ts.map +1 -0
- package/dist/modules/deploy/commands/deploy-status-action.js +153 -0
- package/dist/modules/deploy/commands/deploy-status-action.js.map +1 -0
- package/dist/modules/deploy/commands/deploy-status.d.ts +6 -0
- package/dist/modules/deploy/commands/deploy-status.d.ts.map +1 -0
- package/dist/modules/deploy/commands/deploy-status.js +13 -0
- package/dist/modules/deploy/commands/deploy-status.js.map +1 -0
- package/dist/modules/deploy/index.d.ts +7 -0
- package/dist/modules/deploy/index.d.ts.map +1 -0
- package/dist/modules/deploy/index.js +7 -0
- package/dist/modules/deploy/index.js.map +1 -0
- package/dist/modules/deploy/services/deploy-svc.d.ts +17 -0
- package/dist/modules/deploy/services/deploy-svc.d.ts.map +1 -0
- package/dist/modules/deploy/services/deploy-svc.js +25 -0
- package/dist/modules/deploy/services/deploy-svc.js.map +1 -0
- package/dist/modules/deploy/utils/vercel-extended.d.ts +63 -0
- package/dist/modules/deploy/utils/vercel-extended.d.ts.map +1 -0
- package/dist/modules/deploy/utils/vercel-extended.js +238 -0
- package/dist/modules/deploy/utils/vercel-extended.js.map +1 -0
- package/dist/modules/plugin/commands/plugin-install.d.ts +3 -0
- package/dist/modules/plugin/commands/plugin-install.d.ts.map +1 -0
- package/dist/modules/plugin/commands/plugin-install.js +45 -0
- package/dist/modules/plugin/commands/plugin-install.js.map +1 -0
- package/dist/modules/plugin/errors/plugin-errors.d.ts +16 -0
- package/dist/modules/plugin/errors/plugin-errors.d.ts.map +1 -0
- package/dist/modules/plugin/errors/plugin-errors.js +39 -0
- package/dist/modules/plugin/errors/plugin-errors.js.map +1 -0
- package/dist/modules/plugin/index.d.ts +6 -0
- package/dist/modules/plugin/index.d.ts.map +1 -0
- package/dist/modules/plugin/index.js +9 -0
- package/dist/modules/plugin/index.js.map +1 -0
- package/dist/modules/plugin/lib/plugin-registry.d.ts +15 -0
- package/dist/modules/plugin/lib/plugin-registry.d.ts.map +1 -0
- package/dist/modules/plugin/lib/plugin-registry.js +47 -0
- package/dist/modules/plugin/lib/plugin-registry.js.map +1 -0
- package/dist/modules/plugin/services/plugin-svc.d.ts +10 -0
- package/dist/modules/plugin/services/plugin-svc.d.ts.map +1 -0
- package/dist/modules/plugin/services/plugin-svc.js +44 -0
- package/dist/modules/plugin/services/plugin-svc.js.map +1 -0
- package/dist/modules/plugin/types/plugin-types.d.ts +19 -0
- package/dist/modules/plugin/types/plugin-types.d.ts.map +1 -0
- package/dist/modules/plugin/types/plugin-types.js +2 -0
- package/dist/modules/plugin/types/plugin-types.js.map +1 -0
- package/dist/modules/startup/commands/create-startup.d.ts +3 -0
- package/dist/modules/startup/commands/create-startup.d.ts.map +1 -0
- package/dist/modules/startup/commands/create-startup.js +43 -0
- package/dist/modules/startup/commands/create-startup.js.map +1 -0
- package/dist/modules/startup/errors/startup-errors.d.ts +13 -0
- package/dist/modules/startup/errors/startup-errors.d.ts.map +1 -0
- package/dist/modules/startup/errors/startup-errors.js +25 -0
- package/dist/modules/startup/errors/startup-errors.js.map +1 -0
- package/dist/modules/startup/index.d.ts +5 -0
- package/dist/modules/startup/index.d.ts.map +1 -0
- package/dist/modules/startup/index.js +7 -0
- package/dist/modules/startup/index.js.map +1 -0
- package/dist/modules/startup/services/startup-service.d.ts +7 -0
- package/dist/modules/startup/services/startup-service.d.ts.map +1 -0
- package/dist/modules/startup/services/startup-service.js +43 -0
- package/dist/modules/startup/services/startup-service.js.map +1 -0
- package/dist/modules/startup/types/startup-types.d.ts +8 -0
- package/dist/modules/startup/types/startup-types.d.ts.map +1 -0
- package/dist/modules/startup/types/startup-types.js +2 -0
- package/dist/modules/startup/types/startup-types.js.map +1 -0
- package/dist/modules/startup/utils/startup-validators.d.ts +8 -0
- package/dist/modules/startup/utils/startup-validators.d.ts.map +1 -0
- package/dist/modules/startup/utils/startup-validators.js +17 -0
- package/dist/modules/startup/utils/startup-validators.js.map +1 -0
- package/dist/modules/workspace/commands/init-workspace.d.ts +3 -0
- package/dist/modules/workspace/commands/init-workspace.d.ts.map +1 -0
- package/dist/modules/workspace/commands/init-workspace.js +54 -0
- package/dist/modules/workspace/commands/init-workspace.js.map +1 -0
- package/dist/modules/workspace/errors/workspace-errors.d.ts +10 -0
- package/dist/modules/workspace/errors/workspace-errors.d.ts.map +1 -0
- package/dist/modules/workspace/errors/workspace-errors.js +19 -0
- package/dist/modules/workspace/errors/workspace-errors.js.map +1 -0
- package/dist/modules/workspace/index.d.ts +5 -0
- package/dist/modules/workspace/index.d.ts.map +1 -0
- package/dist/modules/workspace/index.js +7 -0
- package/dist/modules/workspace/index.js.map +1 -0
- package/dist/modules/workspace/services/workspace-service.d.ts +6 -0
- package/dist/modules/workspace/services/workspace-service.d.ts.map +1 -0
- package/dist/modules/workspace/services/workspace-service.js +46 -0
- package/dist/modules/workspace/services/workspace-service.js.map +1 -0
- package/dist/modules/workspace/types/workspace-types.d.ts +8 -0
- package/dist/modules/workspace/types/workspace-types.d.ts.map +1 -0
- package/dist/modules/workspace/types/workspace-types.js +2 -0
- package/dist/modules/workspace/types/workspace-types.js.map +1 -0
- package/dist/modules/workspace/utils/workspace-validators.d.ts +8 -0
- package/dist/modules/workspace/utils/workspace-validators.d.ts.map +1 -0
- package/dist/modules/workspace/utils/workspace-validators.js +17 -0
- package/dist/modules/workspace/utils/workspace-validators.js.map +1 -0
- package/dist/plugins/theme/package.json +32 -0
- package/dist/plugins/theme/plugin.json +9 -0
- package/dist/plugins/theme/src/generator.ts +92 -0
- package/dist/plugins/theme/src/utils/config-modifier.ts +142 -0
- package/dist/plugins/theme/src/utils/css-modifier.ts +89 -0
- package/dist/plugins/theme/templates/app/theme-test/page.tsx +156 -0
- package/dist/plugins/theme/templates/src/modules/theme/README.md +209 -0
- package/dist/plugins/theme/templates/src/modules/theme/config/brand.css +23 -0
- package/dist/plugins/theme/tsconfig.json +14 -0
- package/dist/plugins/theme/tsup.config.ts +10 -0
- package/dist/templates/startup/apps/.gitkeep +8 -0
- package/dist/templates/templates/startup/apps/.gitkeep +8 -0
- package/dist/templates/templates/workspace/.eslintignore +7 -0
- package/dist/templates/templates/workspace/.eslintrc.js +38 -0
- package/dist/templates/templates/workspace/.husky/pre-push +24 -0
- package/dist/templates/templates/workspace/.launch77/workspace.json +3 -0
- package/dist/templates/templates/workspace/.lintstagedrc.json +4 -0
- package/dist/templates/templates/workspace/.prettierrc +9 -0
- package/dist/templates/templates/workspace/README.md +62 -0
- package/dist/templates/templates/workspace/app-templates/.gitkeep +1 -0
- package/dist/templates/templates/workspace/apps/.gitkeep +1 -0
- package/dist/templates/templates/workspace/libraries/.gitkeep +1 -0
- package/dist/templates/templates/workspace/package.json +31 -0
- package/dist/templates/templates/workspace/plugins/.gitkeep +1 -0
- package/dist/templates/templates/workspace/tsconfig.json +22 -0
- package/dist/templates/templates/workspace/turbo.json +25 -0
- package/dist/templates/workspace/.launch77/workspace.json +3 -0
- package/dist/templates/workspace/README.md +62 -0
- package/dist/templates/workspace/app-templates/.gitkeep +1 -0
- package/dist/templates/workspace/apps/.gitkeep +1 -0
- package/dist/templates/workspace/libraries/.gitkeep +1 -0
- package/dist/templates/workspace/package.json +21 -0
- package/dist/templates/workspace/plugins/.gitkeep +1 -0
- package/dist/templates/workspace/tsconfig.json +22 -0
- package/dist/templates/workspace/turbo.json +25 -0
- package/dist/utils/launch77-context.d.ts +16 -0
- package/dist/utils/launch77-context.d.ts.map +1 -0
- package/dist/utils/launch77-context.js +111 -0
- package/dist/utils/launch77-context.js.map +1 -0
- package/dist/utils/launch77-validation.d.ts +17 -0
- package/dist/utils/launch77-validation.d.ts.map +1 -0
- package/dist/utils/launch77-validation.js +44 -0
- package/dist/utils/launch77-validation.js.map +1 -0
- package/dist/utils/monorepo.d.ts +19 -0
- package/dist/utils/monorepo.d.ts.map +1 -0
- package/dist/utils/monorepo.js +100 -0
- package/dist/utils/monorepo.js.map +1 -0
- package/dist/utils/validation.d.ts +32 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +92 -0
- package/dist/utils/validation.js.map +1 -0
- package/dist/utils/version.d.ts +7 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +16 -0
- package/dist/utils/version.js.map +1 -0
- package/launch77-cli-1.2.0.tgz +0 -0
- package/package.json +48 -0
- package/src/cli.ts +34 -0
- package/src/infrastructure/filesystem.ts +69 -0
- package/src/infrastructure/npm.ts +18 -0
- package/src/infrastructure/template-engine.ts +48 -0
- package/src/infrastructure/template-generator.ts +26 -0
- package/src/infrastructure/template.ts +66 -0
- package/src/modules/app/commands/create-app.ts +60 -0
- package/src/modules/app/commands/delete-app.ts +123 -0
- package/src/modules/app/commands/generate-manifest.ts +75 -0
- package/src/modules/app/commands/validate-manifest.ts +78 -0
- package/src/modules/app/errors/app-errors.ts +41 -0
- package/src/modules/app/index.ts +16 -0
- package/src/modules/app/lib/manifest-schema.ts +65 -0
- package/src/modules/app/services/app-svc.ts +115 -0
- package/src/modules/app/services/manifest-svc.ts +144 -0
- package/src/modules/app/types/app-types.ts +27 -0
- package/src/modules/catalog/commands/generate.ts +115 -0
- package/src/modules/catalog/commands/scan.ts +48 -0
- package/src/modules/catalog/config/catalog-config.test.ts +404 -0
- package/src/modules/catalog/config/catalog-config.ts +130 -0
- package/src/modules/catalog/index.ts +9 -0
- package/src/modules/catalog/parsers/typescript-parser.ts +93 -0
- package/src/modules/catalog/scanners/metadata-extractor.ts +134 -0
- package/src/modules/catalog/scanners/metadata-validator.ts +67 -0
- package/src/modules/catalog/scanners/ui-component-scanner.ts +185 -0
- package/src/modules/catalog/schemas/catalog-ui-components.schema.json +145 -0
- package/src/modules/catalog/services/catalog-generator-svc.test.ts +233 -0
- package/src/modules/catalog/services/catalog-generator-svc.ts +124 -0
- package/src/modules/catalog/services/catalog-svc.ts +222 -0
- package/src/modules/catalog/types/catalog-types.ts +149 -0
- package/src/modules/catalog/utils/examples-generator.ts +68 -0
- package/src/modules/catalog/utils/quality-reporter.ts +76 -0
- package/src/modules/catalog/validators/component-name-validator.test.ts +233 -0
- package/src/modules/catalog/validators/component-name-validator.ts +43 -0
- package/src/modules/deploy/commands/deploy-init-action.ts +247 -0
- package/src/modules/deploy/commands/deploy-init.ts +19 -0
- package/src/modules/deploy/commands/deploy-logs-action.ts +135 -0
- package/src/modules/deploy/commands/deploy-logs.ts +20 -0
- package/src/modules/deploy/commands/deploy-status-action.ts +175 -0
- package/src/modules/deploy/commands/deploy-status.ts +16 -0
- package/src/modules/deploy/index.ts +7 -0
- package/src/modules/deploy/services/deploy-svc.ts +31 -0
- package/src/modules/deploy/utils/vercel-extended.ts +286 -0
- package/src/modules/plugin/commands/plugin-install.ts +51 -0
- package/src/modules/plugin/errors/plugin-errors.ts +44 -0
- package/src/modules/plugin/index.ts +14 -0
- package/src/modules/plugin/lib/launch77-workspace.code-workspace +14 -0
- package/src/modules/plugin/lib/plugin-registry.ts +59 -0
- package/src/modules/plugin/services/plugin-svc.ts +53 -0
- package/src/modules/plugin/types/plugin-types.ts +21 -0
- package/src/modules/startup/commands/create-startup.ts +53 -0
- package/src/modules/startup/errors/startup-errors.ts +23 -0
- package/src/modules/startup/index.ts +11 -0
- package/src/modules/startup/services/startup-service.ts +57 -0
- package/src/modules/startup/types/startup-types.ts +8 -0
- package/src/modules/startup/utils/startup-validators.ts +19 -0
- package/src/modules/workspace/commands/init-workspace.ts +63 -0
- package/src/modules/workspace/errors/workspace-errors.ts +16 -0
- package/src/modules/workspace/index.ts +11 -0
- package/src/modules/workspace/services/workspace-service.ts +59 -0
- package/src/modules/workspace/types/workspace-types.ts +8 -0
- package/src/modules/workspace/utils/workspace-validators.ts +19 -0
- package/src/utils/launch77-context.ts +152 -0
- package/src/utils/launch77-validation.ts +59 -0
- package/src/utils/monorepo.ts +137 -0
- package/src/utils/validation.ts +117 -0
- package/src/utils/version.ts +16 -0
- package/templates/startup/apps/.gitkeep +8 -0
- package/templates/workspace/.eslintignore +7 -0
- package/templates/workspace/.eslintrc.js +38 -0
- package/templates/workspace/.husky/pre-push +24 -0
- package/templates/workspace/.launch77/workspace.json +3 -0
- package/templates/workspace/.lintstagedrc.json +4 -0
- package/templates/workspace/.prettierrc +9 -0
- package/templates/workspace/README.md +62 -0
- package/templates/workspace/app-templates/.gitkeep +1 -0
- package/templates/workspace/apps/.gitkeep +1 -0
- package/templates/workspace/libraries/.gitkeep +1 -0
- package/templates/workspace/package.json +31 -0
- package/templates/workspace/plugins/.gitkeep +1 -0
- package/templates/workspace/tsconfig.json +22 -0
- package/templates/workspace/turbo.json +25 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { InvalidStartupNameError } from '../errors/startup-errors.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Validate startup name format
|
|
5
|
+
* - Must be lowercase
|
|
6
|
+
* - Can only contain lowercase letters, numbers, and hyphens
|
|
7
|
+
* - Cannot contain spaces, uppercase, underscores, periods, or special characters
|
|
8
|
+
*/
|
|
9
|
+
export function validateStartupName(name: string): void {
|
|
10
|
+
if (!name || name.trim().length === 0) {
|
|
11
|
+
throw new Error('Startup name cannot be empty')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const validPattern = /^[a-z0-9-]+$/
|
|
15
|
+
|
|
16
|
+
if (!validPattern.test(name)) {
|
|
17
|
+
throw new InvalidStartupNameError(name)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as path from 'path'
|
|
2
|
+
|
|
3
|
+
import { input } from '@inquirer/prompts'
|
|
4
|
+
import chalk from 'chalk'
|
|
5
|
+
import { Command } from 'commander'
|
|
6
|
+
import ora from 'ora'
|
|
7
|
+
|
|
8
|
+
import { WorkspaceAlreadyExistsError, InvalidWorkspaceNameError } from '../errors/workspace-errors.js'
|
|
9
|
+
import { WorkspaceService } from '../services/workspace-service.js'
|
|
10
|
+
|
|
11
|
+
export function initWorkspaceCommand(): Command {
|
|
12
|
+
return new Command('init')
|
|
13
|
+
.argument('[name]', 'Name of the workspace (lowercase, alphanumeric, hyphens)')
|
|
14
|
+
.description('Initialize a new Launch77 workspace')
|
|
15
|
+
.action(async (name?: string) => {
|
|
16
|
+
// If no name provided, prompt for it
|
|
17
|
+
let workspaceName = name
|
|
18
|
+
if (!workspaceName) {
|
|
19
|
+
workspaceName = await input({
|
|
20
|
+
message: 'Workspace name:',
|
|
21
|
+
validate: (value) => {
|
|
22
|
+
if (!value || value.trim().length === 0) {
|
|
23
|
+
return 'Workspace name cannot be empty'
|
|
24
|
+
}
|
|
25
|
+
const validPattern = /^[a-z0-9-]+$/
|
|
26
|
+
if (!validPattern.test(value)) {
|
|
27
|
+
return 'Workspace name must be lowercase, alphanumeric, and hyphens only'
|
|
28
|
+
}
|
|
29
|
+
return true
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
console.log(chalk.blue(`\n🚀 Initializing workspace: ${workspaceName}\n`))
|
|
35
|
+
|
|
36
|
+
const service = new WorkspaceService()
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const spinner = ora('Creating workspace structure...').start()
|
|
40
|
+
|
|
41
|
+
const result = await service.initWorkspace({ name: workspaceName }, process.cwd())
|
|
42
|
+
|
|
43
|
+
spinner.succeed('Workspace structure created')
|
|
44
|
+
|
|
45
|
+
// Success message
|
|
46
|
+
const cwd = process.cwd()
|
|
47
|
+
console.log(chalk.green(`\n✅ Workspace created successfully at ${path.relative(cwd, result.workspacePath)}\n`))
|
|
48
|
+
console.log(chalk.gray(`Next steps:\n` + ` 1. cd ${workspaceName}\n` + ` 2. npm install\n` + ` 3. Create your first app with launch77 commands\n`))
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (error instanceof WorkspaceAlreadyExistsError) {
|
|
51
|
+
console.error(chalk.red(`\n❌ ${error.message}\n`))
|
|
52
|
+
process.exit(1)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (error instanceof InvalidWorkspaceNameError) {
|
|
56
|
+
console.error(chalk.red(`\n❌ ${error.message}\n`))
|
|
57
|
+
process.exit(1)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
throw error
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export class WorkspaceAlreadyExistsError extends Error {
|
|
2
|
+
constructor(
|
|
3
|
+
public workspaceName: string,
|
|
4
|
+
public workspacePath: string
|
|
5
|
+
) {
|
|
6
|
+
super(`Workspace ${workspaceName} already exists at ${workspacePath}`)
|
|
7
|
+
this.name = 'WorkspaceAlreadyExistsError'
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class InvalidWorkspaceNameError extends Error {
|
|
12
|
+
constructor(public workspaceName: string) {
|
|
13
|
+
super(`Invalid workspace name: "${workspaceName}"\n\n` + `Workspace names must:\n` + ` • Be lowercase only\n` + ` • Can only contain: lowercase letters, numbers, and hyphens\n` + ` • Cannot contain: spaces, uppercase, underscores, periods, or special characters\n\n` + `Valid examples:\n` + ` ✓ my-workspace\n` + ` ✓ myworkspace\n` + ` ✓ workspace123\n\n` + `Invalid examples:\n` + ` ✗ MyWorkspace (uppercase)\n` + ` ✗ my_workspace (underscores)\n` + ` ✗ my.workspace (periods)\n` + ` ✗ my workspace (spaces)`)
|
|
14
|
+
this.name = 'InvalidWorkspaceNameError'
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Services
|
|
2
|
+
export { WorkspaceService } from './services/workspace-service.js'
|
|
3
|
+
|
|
4
|
+
// Types
|
|
5
|
+
export type { InitWorkspaceRequest, InitWorkspaceResult } from './types/workspace-types.js'
|
|
6
|
+
|
|
7
|
+
// Errors
|
|
8
|
+
export { WorkspaceAlreadyExistsError, InvalidWorkspaceNameError } from './errors/workspace-errors.js'
|
|
9
|
+
|
|
10
|
+
// Command
|
|
11
|
+
export { initWorkspaceCommand } from './commands/init-workspace.js'
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as path from 'path'
|
|
2
|
+
import { fileURLToPath } from 'url'
|
|
3
|
+
|
|
4
|
+
import * as filesystem from '../../../infrastructure/filesystem.js'
|
|
5
|
+
import { WorkspaceAlreadyExistsError } from '../errors/workspace-errors.js'
|
|
6
|
+
import { validateWorkspaceName } from '../utils/workspace-validators.js'
|
|
7
|
+
|
|
8
|
+
import type { InitWorkspaceRequest, InitWorkspaceResult } from '../types/workspace-types.js'
|
|
9
|
+
|
|
10
|
+
export class WorkspaceService {
|
|
11
|
+
async initWorkspace(request: InitWorkspaceRequest, cwd: string): Promise<InitWorkspaceResult> {
|
|
12
|
+
const { name } = request
|
|
13
|
+
|
|
14
|
+
// 1. Validate name
|
|
15
|
+
validateWorkspaceName(name)
|
|
16
|
+
|
|
17
|
+
// 2. Determine workspace path (relative to current directory)
|
|
18
|
+
const workspacePath = path.join(cwd, name)
|
|
19
|
+
|
|
20
|
+
// 3. Check if workspace already exists
|
|
21
|
+
if (await filesystem.exists(workspacePath)) {
|
|
22
|
+
throw new WorkspaceAlreadyExistsError(name, workspacePath)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// 4. Copy template
|
|
26
|
+
const templatePath = this.getTemplatePath()
|
|
27
|
+
await filesystem.copy(templatePath, workspacePath)
|
|
28
|
+
|
|
29
|
+
// 5. Update package.json with workspace name
|
|
30
|
+
const packageJsonPath = path.join(workspacePath, 'package.json')
|
|
31
|
+
const packageJson = await filesystem.readJSON<{ name?: string }>(packageJsonPath)
|
|
32
|
+
packageJson.name = name
|
|
33
|
+
await filesystem.writeJSON(packageJsonPath, packageJson)
|
|
34
|
+
|
|
35
|
+
// 6. Verify workspace.json was created
|
|
36
|
+
const workspaceJsonPath = path.join(workspacePath, '.launch77', 'workspace.json')
|
|
37
|
+
if (!(await filesystem.exists(workspaceJsonPath))) {
|
|
38
|
+
throw new Error('Failed to create workspace manifest (.launch77/workspace.json)')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
workspacePath,
|
|
43
|
+
name,
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private getTemplatePath(): string {
|
|
48
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
49
|
+
const __dirname = path.dirname(__filename)
|
|
50
|
+
|
|
51
|
+
// From src/modules/workspace/services/ or dist/modules/workspace/services/ up to cli root
|
|
52
|
+
const isDev = __filename.includes('/src/')
|
|
53
|
+
const cliRoot = isDev
|
|
54
|
+
? path.join(__dirname, '../../../..') // src/modules/workspace/services -> cli root
|
|
55
|
+
: path.join(__dirname, '../../../..') // dist/modules/workspace/services -> cli root
|
|
56
|
+
|
|
57
|
+
return path.join(cliRoot, 'templates/workspace')
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { InvalidWorkspaceNameError } from '../errors/workspace-errors.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Validate workspace name format
|
|
5
|
+
* - Must be lowercase
|
|
6
|
+
* - Can only contain lowercase letters, numbers, and hyphens
|
|
7
|
+
* - Cannot contain spaces, uppercase, underscores, periods, or special characters
|
|
8
|
+
*/
|
|
9
|
+
export function validateWorkspaceName(name: string): void {
|
|
10
|
+
if (!name || name.trim().length === 0) {
|
|
11
|
+
throw new Error('Workspace name cannot be empty')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const validPattern = /^[a-z0-9-]+$/
|
|
15
|
+
|
|
16
|
+
if (!validPattern.test(name)) {
|
|
17
|
+
throw new InvalidWorkspaceNameError(name)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import * as path from 'path'
|
|
2
|
+
|
|
3
|
+
import fs from 'fs-extra'
|
|
4
|
+
|
|
5
|
+
export type Launch77LocationType = 'workspace-root' | 'workspace-app' | 'unknown'
|
|
6
|
+
|
|
7
|
+
export interface Launch77Context {
|
|
8
|
+
isValid: boolean
|
|
9
|
+
locationType: Launch77LocationType
|
|
10
|
+
workspaceRoot: string
|
|
11
|
+
appsDir: string
|
|
12
|
+
workspaceVersion: string
|
|
13
|
+
workspaceName: string
|
|
14
|
+
appName?: string
|
|
15
|
+
packageName: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const WORKSPACE_MANIFEST = '.launch77/workspace.json'
|
|
19
|
+
|
|
20
|
+
interface WorkspaceManifest {
|
|
21
|
+
version: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Check if a directory contains a Launch77 workspace manifest
|
|
26
|
+
*/
|
|
27
|
+
async function isWorkspaceRoot(dir: string): Promise<boolean> {
|
|
28
|
+
const manifestPath = path.join(dir, WORKSPACE_MANIFEST)
|
|
29
|
+
return await fs.pathExists(manifestPath)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Read workspace manifest
|
|
34
|
+
*/
|
|
35
|
+
async function readWorkspaceManifest(workspaceRoot: string): Promise<WorkspaceManifest> {
|
|
36
|
+
const manifestPath = path.join(workspaceRoot, WORKSPACE_MANIFEST)
|
|
37
|
+
return await fs.readJSON(manifestPath)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Find the workspace root by traversing up from a starting directory
|
|
42
|
+
* Looks for .launch77/workspace.json manifest file
|
|
43
|
+
*/
|
|
44
|
+
async function findWorkspaceRoot(startDir: string): Promise<string | null> {
|
|
45
|
+
let currentDir = path.resolve(startDir)
|
|
46
|
+
const rootDir = path.parse(currentDir).root
|
|
47
|
+
|
|
48
|
+
while (currentDir !== rootDir) {
|
|
49
|
+
if (await isWorkspaceRoot(currentDir)) {
|
|
50
|
+
return currentDir
|
|
51
|
+
}
|
|
52
|
+
currentDir = path.dirname(currentDir)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface ParsedLocation {
|
|
59
|
+
locationType: Launch77LocationType
|
|
60
|
+
appName?: string
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Parse the directory structure to determine location context
|
|
65
|
+
* Based on patterns:
|
|
66
|
+
* - apps/[name] → workspace-app
|
|
67
|
+
* - (empty or root) → workspace-root
|
|
68
|
+
*/
|
|
69
|
+
function parseLocationFromPath(cwdPath: string, workspaceRoot: string): ParsedLocation {
|
|
70
|
+
const relativePath = path.relative(workspaceRoot, cwdPath)
|
|
71
|
+
|
|
72
|
+
// At workspace root
|
|
73
|
+
if (!relativePath || relativePath === '.') {
|
|
74
|
+
return { locationType: 'workspace-root' }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const parts = relativePath.split(path.sep)
|
|
78
|
+
|
|
79
|
+
// apps/[app-name]/...
|
|
80
|
+
if (parts[0] === 'apps' && parts.length >= 2) {
|
|
81
|
+
return {
|
|
82
|
+
locationType: 'workspace-app',
|
|
83
|
+
appName: parts[1],
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Somewhere else in workspace (libraries, plugins, etc.)
|
|
88
|
+
// Still considered workspace-root for now
|
|
89
|
+
return { locationType: 'workspace-root' }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Detect the Launch77 workspace context from the current working directory
|
|
94
|
+
*/
|
|
95
|
+
export async function detectLaunch77Context(cwd: string): Promise<Launch77Context> {
|
|
96
|
+
const resolvedCwd = path.resolve(cwd)
|
|
97
|
+
|
|
98
|
+
// Find the workspace root
|
|
99
|
+
const workspaceRoot = await findWorkspaceRoot(resolvedCwd)
|
|
100
|
+
|
|
101
|
+
if (!workspaceRoot) {
|
|
102
|
+
return {
|
|
103
|
+
isValid: false,
|
|
104
|
+
locationType: 'unknown',
|
|
105
|
+
workspaceRoot: '',
|
|
106
|
+
appsDir: '',
|
|
107
|
+
workspaceVersion: '',
|
|
108
|
+
workspaceName: '',
|
|
109
|
+
packageName: '',
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Read workspace manifest
|
|
114
|
+
let manifest: WorkspaceManifest
|
|
115
|
+
try {
|
|
116
|
+
manifest = await readWorkspaceManifest(workspaceRoot)
|
|
117
|
+
} catch (error) {
|
|
118
|
+
// Manifest exists (we found it) but couldn't read it
|
|
119
|
+
return {
|
|
120
|
+
isValid: false,
|
|
121
|
+
locationType: 'unknown',
|
|
122
|
+
workspaceRoot: '',
|
|
123
|
+
appsDir: '',
|
|
124
|
+
workspaceVersion: '',
|
|
125
|
+
workspaceName: '',
|
|
126
|
+
packageName: '',
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Parse the directory structure to determine location
|
|
131
|
+
const parsed = parseLocationFromPath(resolvedCwd, workspaceRoot)
|
|
132
|
+
|
|
133
|
+
// Workspace name is the directory name
|
|
134
|
+
const workspaceName = path.basename(workspaceRoot)
|
|
135
|
+
|
|
136
|
+
// Apps directory is always {workspaceRoot}/apps
|
|
137
|
+
const appsDir = path.join(workspaceRoot, 'apps')
|
|
138
|
+
|
|
139
|
+
// Package name: @{workspaceName}/{appName} (if in app)
|
|
140
|
+
const packageName = parsed.appName ? `@${workspaceName}/${parsed.appName}` : ''
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
isValid: true,
|
|
144
|
+
locationType: parsed.locationType,
|
|
145
|
+
workspaceRoot,
|
|
146
|
+
appsDir,
|
|
147
|
+
workspaceVersion: manifest.version,
|
|
148
|
+
workspaceName,
|
|
149
|
+
appName: parsed.appName,
|
|
150
|
+
packageName,
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Launch77Context } from './launch77-context.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Validation result with helpful error messages
|
|
5
|
+
*/
|
|
6
|
+
export interface ValidationResult {
|
|
7
|
+
valid: boolean
|
|
8
|
+
errorMessage?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Validate that we're in a workspace context for app:create
|
|
13
|
+
*/
|
|
14
|
+
export function validateWorkspaceContext(context: Launch77Context): ValidationResult {
|
|
15
|
+
if (!context.isValid) {
|
|
16
|
+
return {
|
|
17
|
+
valid: false,
|
|
18
|
+
errorMessage: 'Must be run from within a Launch77 workspace.\n\n' + 'Create a workspace first:\n' + ' launch77 init my-workspace\n' + ' cd my-workspace',
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (context.locationType !== 'workspace-root' && context.locationType !== 'workspace-app') {
|
|
23
|
+
return {
|
|
24
|
+
valid: false,
|
|
25
|
+
errorMessage: 'Must be run from workspace root or app directory',
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return { valid: true }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Validate that we're inside an app directory (for plugin:install - future use)
|
|
34
|
+
*/
|
|
35
|
+
export function validateAppContext(context: Launch77Context): ValidationResult {
|
|
36
|
+
if (!context.isValid) {
|
|
37
|
+
return {
|
|
38
|
+
valid: false,
|
|
39
|
+
errorMessage: 'Must be run from within a Launch77 workspace app directory',
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Must be inside an app, not just workspace root
|
|
44
|
+
if (context.locationType !== 'workspace-app') {
|
|
45
|
+
return {
|
|
46
|
+
valid: false,
|
|
47
|
+
errorMessage: 'plugin:install must be run from within an app directory.\n\n' + 'Navigate to an app:\n' + ' cd apps/<app-name>',
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!context.appName) {
|
|
52
|
+
return {
|
|
53
|
+
valid: false,
|
|
54
|
+
errorMessage: 'Could not determine app name from current directory.\nThis is a bug. Please report it.',
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return { valid: true }
|
|
59
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import * as path from 'path'
|
|
2
|
+
|
|
3
|
+
import fs from 'fs-extra'
|
|
4
|
+
|
|
5
|
+
export type LocationType = 'monorepo-root' | 'startup-root' | 'startup-app' | 'unknown'
|
|
6
|
+
|
|
7
|
+
export interface MonorepoContext {
|
|
8
|
+
isValid: boolean
|
|
9
|
+
location: LocationType
|
|
10
|
+
monorepoRoot: string
|
|
11
|
+
appsDir: string | null
|
|
12
|
+
// Specific context info
|
|
13
|
+
startupName?: string
|
|
14
|
+
appName?: string
|
|
15
|
+
packageName?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const MONOREPO_ROOT_MARKER = '.launch-monorepo-root.md'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Check if a directory is the monorepo root by looking for the marker file
|
|
22
|
+
*/
|
|
23
|
+
async function isMonorepoRoot(dir: string): Promise<boolean> {
|
|
24
|
+
const markerPath = path.join(dir, MONOREPO_ROOT_MARKER)
|
|
25
|
+
return await fs.pathExists(markerPath)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Find the monorepo root by traversing up from a starting directory
|
|
30
|
+
* Looks for .launch-monorepo-root.md marker file
|
|
31
|
+
*/
|
|
32
|
+
async function findMonorepoRoot(startDir: string): Promise<string | null> {
|
|
33
|
+
let currentDir = path.resolve(startDir)
|
|
34
|
+
const rootDir = path.parse(currentDir).root
|
|
35
|
+
|
|
36
|
+
while (currentDir !== rootDir) {
|
|
37
|
+
if (await isMonorepoRoot(currentDir)) {
|
|
38
|
+
return currentDir
|
|
39
|
+
}
|
|
40
|
+
currentDir = path.dirname(currentDir)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return null
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface ParsedLocation {
|
|
47
|
+
location: LocationType
|
|
48
|
+
startupName?: string
|
|
49
|
+
appName?: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Parse the directory structure to determine location context
|
|
54
|
+
* Based on patterns:
|
|
55
|
+
* - startups/[name] → startup-root
|
|
56
|
+
* - startups/[name]/apps/[app] → startup-app
|
|
57
|
+
* - (empty or root) → monorepo-root
|
|
58
|
+
*/
|
|
59
|
+
function parseLocationFromPath(cwdPath: string, monorepoRoot: string): ParsedLocation {
|
|
60
|
+
const relativePath = path.relative(monorepoRoot, cwdPath)
|
|
61
|
+
|
|
62
|
+
// At monorepo root
|
|
63
|
+
if (!relativePath || relativePath === '.') {
|
|
64
|
+
return { location: 'monorepo-root' }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const parts = relativePath.split(path.sep)
|
|
68
|
+
|
|
69
|
+
// startups/[name]/...
|
|
70
|
+
if (parts[0] === 'startups' && parts.length >= 2) {
|
|
71
|
+
const startupName = parts[1]
|
|
72
|
+
|
|
73
|
+
// startups/[name]/apps/[app]
|
|
74
|
+
if (parts.length >= 4 && parts[2] === 'apps') {
|
|
75
|
+
return {
|
|
76
|
+
location: 'startup-app',
|
|
77
|
+
startupName,
|
|
78
|
+
appName: parts[3],
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// startups/[name]
|
|
83
|
+
return {
|
|
84
|
+
location: 'startup-root',
|
|
85
|
+
startupName,
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return { location: 'unknown' }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Determine the apps directory based on location context
|
|
94
|
+
* Returns null for invalid locations (e.g., monorepo-root, unknown)
|
|
95
|
+
*/
|
|
96
|
+
function getAppsDir(monorepoRoot: string, location: LocationType, startupName?: string): string | null {
|
|
97
|
+
if (location === 'startup-root' || location === 'startup-app') {
|
|
98
|
+
return path.join(monorepoRoot, 'startups', startupName!, 'apps')
|
|
99
|
+
}
|
|
100
|
+
return null
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Detect the monorepo context from the current working directory
|
|
105
|
+
*
|
|
106
|
+
* @deprecated Use detectLaunch77Context() from launch77-context.ts instead.
|
|
107
|
+
* This function is maintained for legacy monorepo structure only.
|
|
108
|
+
* New workspaces created with `launch77 init` should use the new context system.
|
|
109
|
+
*/
|
|
110
|
+
export async function detectMonorepoContext(cwd: string): Promise<MonorepoContext> {
|
|
111
|
+
const resolvedCwd = path.resolve(cwd)
|
|
112
|
+
|
|
113
|
+
// Find the monorepo root
|
|
114
|
+
const monorepoRoot = await findMonorepoRoot(resolvedCwd)
|
|
115
|
+
|
|
116
|
+
if (!monorepoRoot) {
|
|
117
|
+
return {
|
|
118
|
+
isValid: false,
|
|
119
|
+
location: 'unknown',
|
|
120
|
+
monorepoRoot: '',
|
|
121
|
+
appsDir: null,
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Parse the directory structure to determine location
|
|
126
|
+
const parsed = parseLocationFromPath(resolvedCwd, monorepoRoot)
|
|
127
|
+
const appsDir = getAppsDir(monorepoRoot, parsed.location, parsed.startupName)
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
isValid: true,
|
|
131
|
+
location: parsed.location,
|
|
132
|
+
monorepoRoot,
|
|
133
|
+
appsDir,
|
|
134
|
+
startupName: parsed.startupName,
|
|
135
|
+
appName: parsed.appName,
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import * as path from 'path'
|
|
2
|
+
|
|
3
|
+
import chalk from 'chalk'
|
|
4
|
+
|
|
5
|
+
import type { MonorepoContext } from './monorepo.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Validation result with helpful error messages
|
|
9
|
+
*/
|
|
10
|
+
export interface ValidationResult {
|
|
11
|
+
valid: boolean
|
|
12
|
+
errorMessage?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Validate that we're in a startup context (for create-app)
|
|
17
|
+
*/
|
|
18
|
+
export function validateStartupContext(context: MonorepoContext): ValidationResult {
|
|
19
|
+
if (!context.isValid) {
|
|
20
|
+
return {
|
|
21
|
+
valid: false,
|
|
22
|
+
errorMessage: 'Must be run from within a Launch77 monorepo (could not find .launch-monorepo-root.md)',
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (context.location !== 'startup-root' && context.location !== 'startup-app') {
|
|
27
|
+
return {
|
|
28
|
+
valid: false,
|
|
29
|
+
errorMessage: `Command must be run from a startup directory.\n\n` + `${chalk.gray('Current location:')} ${context.location}\n` + `${chalk.gray('Expected:')} Within startups/<startup-name>/\n\n` + `${chalk.yellow('Navigate to a startup first:')}\n` + ` cd startups/<startup-name>/\n\n` + `${chalk.yellow('Or create a startup:')}\n` + ` launch77 create-startup <startup-name>`,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!context.appsDir) {
|
|
34
|
+
return {
|
|
35
|
+
valid: false,
|
|
36
|
+
errorMessage: `Could not determine apps directory for location: ${context.location}\n` + `This is a bug. Please report it.`,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return { valid: true }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Validate that we're inside an app directory (for plugin:install)
|
|
45
|
+
*/
|
|
46
|
+
export function validateAppContext(context: MonorepoContext): ValidationResult {
|
|
47
|
+
if (!context.isValid) {
|
|
48
|
+
return {
|
|
49
|
+
valid: false,
|
|
50
|
+
errorMessage: 'Must be run from within a Launch77 monorepo (could not find .launch-monorepo-root.md)',
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Must be inside an app, not just a startup
|
|
55
|
+
if (context.location !== 'startup-app') {
|
|
56
|
+
return {
|
|
57
|
+
valid: false,
|
|
58
|
+
errorMessage: `plugin:install must be run from within an app directory.\n\n` + `${chalk.gray('Current location:')} ${context.location}\n` + `${chalk.gray('Expected:')} startups/<startup-name>/apps/<app-name>/\n\n` + `${chalk.yellow('Navigate to an app directory:')}\n` + ` cd startups/<startup-name>/apps/<app-name>/\n\n` + `${chalk.yellow('Or create an app first:')}\n` + ` cd startups/<startup-name>/\n` + ` launch77 app:create api <app-name>`,
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!context.appName) {
|
|
63
|
+
return {
|
|
64
|
+
valid: false,
|
|
65
|
+
errorMessage: `Could not determine app name from current directory.\n` + `This is a bug. Please report it.`,
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return { valid: true }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get the app directory path
|
|
74
|
+
* Returns null if not in an app context
|
|
75
|
+
*/
|
|
76
|
+
export function getAppDirectory(context: MonorepoContext): string | null {
|
|
77
|
+
if (context.location !== 'startup-app') {
|
|
78
|
+
return null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!context.monorepoRoot || !context.startupName || !context.appName) {
|
|
82
|
+
return null
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return path.join(context.monorepoRoot, 'startups', context.startupName, 'apps', context.appName)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Validate app name format
|
|
90
|
+
*
|
|
91
|
+
* App names must follow Launch77 naming conventions:
|
|
92
|
+
* - Must be lowercase
|
|
93
|
+
* - Must start with a letter [a-z]
|
|
94
|
+
* - Can only contain lowercase letters, numbers, and hyphens
|
|
95
|
+
* - Cannot contain spaces, uppercase, underscores, periods, or special characters
|
|
96
|
+
*/
|
|
97
|
+
export function validateAppName(appName: string): ValidationResult {
|
|
98
|
+
// Check for empty name
|
|
99
|
+
if (!appName || appName.trim().length === 0) {
|
|
100
|
+
return {
|
|
101
|
+
valid: false,
|
|
102
|
+
errorMessage: 'App name cannot be empty',
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Launch77 naming convention: lowercase, letters, numbers, hyphens only
|
|
107
|
+
const validPattern = /^[a-z][a-z0-9-]*$/
|
|
108
|
+
|
|
109
|
+
if (!validPattern.test(appName)) {
|
|
110
|
+
return {
|
|
111
|
+
valid: false,
|
|
112
|
+
errorMessage: `Invalid app name: "${appName}"\n\n` + `${chalk.gray('App names must:')}\n` + ` • Be lowercase only\n` + ` • Start with a letter\n` + ` • Can only contain: lowercase letters, numbers, and hyphens\n` + ` • Cannot contain: spaces, uppercase, underscores, periods, or special characters\n\n` + `${chalk.yellow('Valid examples:')}\n` + ` ✓ my-app\n` + ` ✓ myapp\n` + ` ✓ myapp123\n` + ` ✓ app-123\n\n` + `${chalk.red('Invalid examples:')}\n` + ` ✗ MyApp (uppercase)\n` + ` ✗ my_app (underscores)\n` + ` ✗ my.app (periods)\n` + ` ✗ my app (spaces)\n` + ` ✗ @myapp (special characters)\n` + ` ✗ -myapp (starts with hyphen)\n` + ` ✗ 123app (starts with number)`,
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return { valid: true }
|
|
117
|
+
}
|