diff --git a/scripts/process-tutorial.js b/scripts/process-tutorial.js index 42f1e820..e1982a64 100644 --- a/scripts/process-tutorial.js +++ b/scripts/process-tutorial.js @@ -97,6 +97,16 @@ async function main() { } } + // Check if there is a custom install command which means there is an external app + if (tutorialConfig.customInstallCmd) { + hasExternalApp = true; + } + + // Check if there is a custom start command which means there is an external app + if (tutorialConfig.customStartCmd) { + hasExternalApp = true; + } + // 5. THE OVERLAY (Apply Starter Files & Config on top of the cloned repo) console.log("๐Ÿ“‚ Applying starter files and configuration overlay..."); @@ -127,6 +137,11 @@ async function main() { } } + // Check for custom setup command + if (tutorialConfig.customSetupCmd) { + hasSetupScript = true; + } + // 6. GENERATE BLANK FILES (Fallback for explicit blank files) if (tutorialConfig.files && Array.isArray(tutorialConfig.files)) { tutorialConfig.files.forEach(fileName => { @@ -153,7 +168,11 @@ async function main() { pkg.scripts["start:tutorial"] = "http-server steps -p 1234 --cors -c-1"; if (hasExternalApp) { - pkg.scripts["postinstall"] = "cd project && npm install"; + if (tutorialConfig.customInstallCmd) { + pkg.scripts["postinstall"] = `cd project && ${tutorialConfig.customInstallCmd}`; + } else { + pkg.scripts["postinstall"] = "cd project && npm install"; + } } fs.writeJsonSync(rootPackageJson, pkg, { spaces: 2 }); @@ -208,7 +227,7 @@ Want to make changes to the lesson, add steps, or update starter files? `; fs.writeFileSync(path.join(targetDir, 'README.md'), readmeContent); - // 10. Cleanup + // 11. Cleanup fs.removeSync(path.join(UPLOADS_DIR, zipFilename)); console.log("๐Ÿงน Cleanup complete. Zip removed from uploads."); } @@ -262,7 +281,21 @@ async function generateDevContainer(name, config, hasExternalApp, hasSetupScript // 2. Prepare the directory if (hasSetupScript) { - commandChain += "echo '' && cd project && node setup-project.js && "; + commandChain += "echo '' && cd project && "; + if (config.customSetupCmd){ + const customSetupCmd = config.customSetupCmd + .split(/\r?\n/) + .map(cmd => cmd.trim()) + .filter(cmd => cmd !== "") + .join(' && '); + if (customSetupCmd){ + commandChain += `${customSetupCmd} && `; + } else { + console.warn("โš ๏ธ Custom Setup Command was provided but resolved to empty. Skipping."); + } + } else { + commandChain += "node setup-project.js && "; + } } else if (hasExternalApp) { commandChain += "cd project && "; } @@ -278,7 +311,8 @@ async function generateDevContainer(name, config, hasExternalApp, hasSetupScript commandChain += `(nohup sh -c "sleep 5 && ${visibilityCmd}" > /dev/null 2>&1 &) && `; // Run the project's start script - commandChain += "npm start"; + const startCmd = config.customStartCmd || "npm start"; + commandChain += startCmd; } else if (config.panels && config.panels.includes('browser')) { // Fallback: If no external app but browser requested, run live-server (from root) if (!hasSetupScript) { diff --git a/toolbar-app/vonage-toolbar/app.ts b/toolbar-app/vonage-toolbar/app.ts index 7d66b26d..9861ba96 100644 --- a/toolbar-app/vonage-toolbar/app.ts +++ b/toolbar-app/vonage-toolbar/app.ts @@ -9,6 +9,9 @@ let tutorial: { capabilities: string[]; version: string; filename: string; + customInstallCmd: string; + customSetupCmd: string; + customStartCmd: string; } = { files: [], openFiles: [], @@ -17,7 +20,10 @@ let tutorial: { starterFiles: [], capabilities: [], version: '', - filename: '' + filename: '', + customInstallCmd: '', + customSetupCmd: '', + customStartCmd: '' }; export default defineToolbarApp({ @@ -42,8 +48,14 @@ export default defineToolbarApp({
Step 2: Load an external repo (optional) - The repo will be loaded in the project folder. If there are any setup scripts needed to run the repo, please put them in the setup-project.js file. + The repo will be loaded in the project folder. +

Default install command is npm install, if you have custom install command, please enter it here: + +

Default setup command is node setup-project.js that will run the code in the setup-project.js file. If you have a custom setup command, please enter it here and make sure to create any files you need: + + +

Default start command is npm start, if you have custom start command, please enter it here:
Step 3: Set Starter Files to be used @@ -138,6 +150,37 @@ export default defineToolbarApp({ saveConfig(); }); + + const customInstallCmdInput = astroToolbarWindow?.querySelector( + '#custom-install-command' + ) as HTMLInputElement; + customInstallCmdInput.value = + tutorial.customInstallCmd !== '' ? tutorial.customInstallCmd : ''; + customInstallCmdInput?.addEventListener('change', (event) => { + tutorial.customInstallCmd = customInstallCmdInput?.value; + saveConfig(); + }); + + const customSetupCmdInput = astroToolbarWindow?.querySelector( + '#custom-setup-command' + ) as HTMLInputElement; + customSetupCmdInput.value = + tutorial.customSetupCmd !== '' ? tutorial.customSetupCmd : ''; + customSetupCmdInput?.addEventListener('change', (event) => { + tutorial.customSetupCmd = customSetupCmdInput?.value; + saveConfig(); + }); + + const customStartCmdInput = astroToolbarWindow?.querySelector( + '#custom-start-command' + ) as HTMLInputElement; + customStartCmdInput.value = + tutorial.customStartCmd !== '' ? tutorial.customStartCmd : ''; + customStartCmdInput?.addEventListener('change', (event) => { + tutorial.customStartCmd = customStartCmdInput?.value; + saveConfig(); + }); + // Always read tutorial-config.json from disk on toolbar open server.send('vonage-app:config-check', {}); @@ -173,6 +216,8 @@ export default defineToolbarApp({ versionInput.value = tutorial.version; repositoryInput.value = tutorial.repository; filenameInput.value = tutorial.filename; + customInstallCmdInput.value = tutorial.customInstallCmd; + customSetupCmdInput.value = tutorial.customSetupCmd; }