@supernovaio/cli
Version:
Supernova.io Command Line Interface
283 lines (281 loc) • 15.2 kB
JavaScript
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="79164005-5c4d-5eb4-b9f4-98e8d135a628")}catch(e){}}();
import inquirer from "inquirer";
import { SETUP_DOCS_URL, SETUP_INTRO_HEADER_ART } from "../../constants.js";
import { colorize, emphasis, formatFullSnapshotPathLink, muted, renderPanel, success, warning } from "../../ui.js";
import { promptContinueSetup, promptScanPackages, promptScanSourcePath, promptTemplateFramework } from "./prompts.js";
import { renderDiscoveredPackages, renderSourceScan } from "./screens.js";
const introDescriptionLinesMuted = new Set([
" Run and upload static analysis of your design system sources for high quality prototypes.",
" Build and upload a sandbox container with your real components, icons, CSS, and templates.",
" The container is your prototyping environment — fully customizable and expandable after setup.",
" Generate your first prototype using your actual product code.",
" Create a container folder with an Angular template on your machine.",
" Follow the setup guide to add your components to the container manually.",
" Upload the container to Supernova so it can be used for prototyping.",
" Generate your first prototype using your actual product code.",
]);
function introPanelLineTone(line, _lineIndex) {
if (line === "✅ Before you start:" || line === "🔍 What will happen next:") {
return "blueBold";
}
if (introDescriptionLinesMuted.has(line)) {
return "gray";
}
return "none";
}
function shouldRunScanForFramework(framework) {
return framework === "React";
}
function scanContextForFramework(framework) {
const shouldRunScan = shouldRunScanForFramework(framework);
return {
analysisProcessingRunId: undefined,
componentPackages: undefined,
discoveredPackages: undefined,
repoPaths: undefined,
scanOutputPaths: undefined,
scanSourcePath: undefined,
shouldRunScan,
shouldUploadScanResults: shouldRunScan,
templateFramework: framework,
};
}
function renderIntroPanel(framework, gitRootWarningLines) {
switch (framework) {
case "React":
return renderPanel("Supernova Create Setup for React", [
...(gitRootWarningLines.length > 0 ? [...gitRootWarningLines, ""] : []),
"Connect your codebase for AI-powered prototyping with your actual components - not generics.",
"",
"✅ Before you start:",
" • Make sure you run this command from your repository root",
" • Docker installed and running",
" • A successful dependency installation (e.g., npm install) is required",
" • npm token ready if your packages are behind a private registry",
"",
"🔍 What will happen next:",
" 1. Codebase analysis",
" Run and upload static analysis of your design system sources for high quality prototypes.",
"",
" 2. Code container setup",
" Build and upload a sandbox container with your real components, icons, CSS, and templates.",
" The container is your prototyping environment — fully customizable and expandable after setup.",
"",
" 3. Create prototype",
" Generate your first prototype using your actual product code.",
"",
`${colorize("🕒 Estimated setup time:", "blue")} Up to 30 minutes`,
"",
`${colorize("📘 Docs:", "blue")} ${SETUP_DOCS_URL}`,
`${colorize("💬 Need help? Contact us:", "blue")} support@supernova.io`,
], { lineTone: introPanelLineTone, titleTone: "blueBold", headerArt: SETUP_INTRO_HEADER_ART });
case "Angular":
return renderPanel("Supernova Create Setup for Angular", [
...(gitRootWarningLines.length > 0 ? [...gitRootWarningLines, ""] : []),
"Connect your codebase for AI-powered prototyping with your actual components - not generics.",
"",
"✅ Before you start:",
" • Make sure you run this command from your repository root",
" • Docker installed and running",
" • npm token ready if your packages are behind a private registry",
"",
"🔍 What will happen next:",
" 1. Code container setup",
" Create a container folder with an Angular template on your machine.",
"",
" 2. Add your design system components to code container",
" Follow the setup guide to add your components to the container manually.",
"",
" 3. Upload container",
" Upload the container to Supernova so it can be used for prototyping.",
"",
" 4. Create prototype",
" Generate your first prototype using your actual product code.",
"",
`${colorize("📘 Docs:", "blue")} ${SETUP_DOCS_URL}`,
`${colorize("💬 Need help? Contact us:", "blue")} support@supernova.io`,
], { lineTone: introPanelLineTone, titleTone: "blueBold", headerArt: SETUP_INTRO_HEADER_ART });
case "Vue":
return renderPanel("Supernova Create Setup for Vue", [
...(gitRootWarningLines.length > 0 ? [...gitRootWarningLines, ""] : []),
"Connect your codebase for AI-powered prototyping with your actual components - not generics.",
"",
"✅ Before you start:",
" • Make sure you run this command from your repository root",
" • Docker installed and running",
" • npm token ready if your packages are behind a private registry",
"",
"🔍 What will happen next:",
" 1. Code container setup",
" Create a container folder with a Vue template on your machine.",
"",
" 2. Add your design system components to code container",
" Follow the setup guide to add your components to the container manually.",
"",
" 3. Upload container",
" Upload the container to Supernova so it can be used for prototyping.",
"",
" 4. Create prototype",
" Generate your first prototype using your actual product code.",
"",
`${colorize("📘 Docs:", "blue")} ${SETUP_DOCS_URL}`,
`${colorize("💬 Need help? Contact us:", "blue")} support@supernova.io`,
], { lineTone: introPanelLineTone, titleTone: "blueBold", headerArt: SETUP_INTRO_HEADER_ART });
}
}
export function createScanHandlers(services) {
return {
async INIT(context) {
const templateFramework = await promptTemplateFramework(context.templateFramework);
let gitRootWarningLines = [];
const parentGitLocation = services.validation.findParentGitDirectoryLocation();
if (parentGitLocation) {
gitRootWarningLines = [
`⚠️ Detected a git repository above the current directory: ${parentGitLocation}`,
"⚠️ For the best setup defaults, run this command from the repository root.",
];
}
services.ui.log("");
services.ui.log(renderIntroPanel(templateFramework, gitRootWarningLines));
await promptContinueSetup(services.ui.error);
return scanContextForFramework(templateFramework);
},
async SELECT_DESIGN_SYSTEM(context) {
services.ui.renderStep("Design system (select)");
const designSystemId = await services.designSystems.promptDesignSystemId();
const workspaceId = await services.designSystems.resolveWorkspaceIdForDesignSystem(designSystemId);
await services.designSystems.ensureWorkspaceSetupEligibility(workspaceId);
await services.designSystems.ensureSandboxTemplateAccess(workspaceId);
services.designSystems.updateSelectedDesignSystem(designSystemId);
services.ui.log(`${emphasis("Selected design system ID:")} ${designSystemId}`);
const scanContext = context.templateFramework ? scanContextForFramework(context.templateFramework) : {};
return {
designSystemId,
...scanContext,
workspaceId,
};
},
async SELECT_SCAN_SOURCE_PATH(context) {
if (!context.shouldRunScan) {
return {
componentPackages: undefined,
discoveredPackages: undefined,
repoPaths: undefined,
scanSourcePath: undefined,
};
}
services.ui.renderStep("Design system codebase analysis");
services.ui.log(renderSourceScan());
const discovery = await promptScanSourcePath({
allowSkip: services.debug,
error: services.ui.error,
log: message => services.ui.log(warning(message)),
});
if (!discovery) {
services.ui.log(muted("Skipping codebase analysis and continuing with container setup."));
return {
componentPackages: undefined,
discoveredPackages: undefined,
repoPaths: undefined,
scanOutputPaths: undefined,
scanSourcePath: undefined,
shouldRunScan: false,
shouldUploadScanResults: false,
};
}
const discoveredPackages = discovery.discoveredPackages ?? [];
services.ui.log(`${emphasis("Source folder:")} ${discovery.scanSourcePath}`);
services.ui.log(`${emphasis("Discovered packages:")} ${discoveredPackages.map(pkg => pkg.name).join(", ")}`);
return {
discoveredPackages,
repoPaths: [discovery.scanSourcePath],
scanSourcePath: discovery.scanSourcePath,
};
},
async SELECT_SCAN_PACKAGES(context) {
if (!context.shouldRunScan) {
return { componentPackages: undefined };
}
services.ui.renderStep("Discovered packages");
services.ui.log(renderDiscoveredPackages());
const discoveredPackages = context.discoveredPackages ?? [];
if (discoveredPackages.length === 0) {
services.ui.error("No discovered packages were available for scan.");
}
const componentPackages = await promptScanPackages(discoveredPackages, context.componentPackages, services.ui.error);
services.ui.log(`${emphasis("Packages to scan:")} ${componentPackages.join(", ")}`);
services.scan.persistAnalyzePackages(componentPackages);
return { componentPackages };
},
async RUN_SCAN(context) {
if (!context.shouldRunScan) {
return { scanOutputPaths: undefined };
}
if (!context.componentPackages?.length)
services.ui.error("Component package names are missing.");
if (!context.scanSourcePath)
services.ui.error("Scan source path is missing.");
if (!context.designSystemId)
services.ui.error("Design system is missing.");
const scanOutputPaths = await services.scan.runScanAgainstSourcePath({
componentPackages: context.componentPackages,
designSystemId: context.designSystemId,
scanSourcePath: context.scanSourcePath,
});
return { repoPaths: [context.scanSourcePath], scanOutputPaths };
},
async REVIEW_SCAN_OUTPUT(context) {
if (!context.shouldRunScan) {
return { shouldUploadScanResults: false };
}
if (!context.scanOutputPaths?.length || !context.componentPackages?.length || !context.designSystemId) {
services.ui.error("Scan output is missing.");
}
services.ui.renderStep("Scan review");
const { scanOutputPaths } = context;
while (true) {
services.ui.log(success("Scan complete."));
const response = await inquirer.prompt([
{
choices: [
{ name: "Upload scan results and continue", value: "upload" },
{ name: "Explore results", value: "explore" },
...(services.debug ? [{ name: "Skip scan upload and continue", value: "skip_upload" }] : []),
],
default: services.debug && context.shouldUploadScanResults === false ? "skip_upload" : "upload",
message: colorize("Next action:", "blue"),
name: "action",
type: "list",
},
]);
if (response.action === "explore") {
services.ui.log("");
services.ui.log("Scan results");
services.ui.log(scanOutputPaths.map(value => ` ${muted("•")} ${formatFullSnapshotPathLink(value)}`).join("\n"));
continue;
}
return {
repoPaths: context.scanSourcePath ? [context.scanSourcePath] : context.repoPaths,
scanOutputPaths,
shouldUploadScanResults: response.action === "upload",
};
}
},
async UPLOAD_SCAN_RESULTS(context) {
if (!context.shouldRunScan || !context.shouldUploadScanResults) {
return undefined;
}
services.ui.renderStep("Upload analysis snapshots");
if (!context.designSystemId || !context.scanOutputPaths?.length || !context.repoPaths?.length) {
services.ui.error("Scan upload context is incomplete.");
}
const uploadResult = await services.scan.uploadAnalysisSnapshots({
designSystemId: context.designSystemId,
scanOutputPaths: context.scanOutputPaths,
});
return { analysisProcessingRunId: uploadResult.processingRunId };
},
};
}
//# sourceMappingURL=index.js.map
//# debugId=79164005-5c4d-5eb4-b9f4-98e8d135a628