From 6265983cfd980651b4f4479020c9bfc94e1c1169 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Fri, 20 Sep 2024 15:13:30 -0400 Subject: [PATCH 01/40] Clean up --- app/src/lib/editor/engine/canvas/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/lib/editor/engine/canvas/index.ts b/app/src/lib/editor/engine/canvas/index.ts index da9a948f9..89078086a 100644 --- a/app/src/lib/editor/engine/canvas/index.ts +++ b/app/src/lib/editor/engine/canvas/index.ts @@ -18,7 +18,10 @@ export class CanvasManager { constructor(private projects: ProjectsManager) { makeAutoObservable(this); + this.listenToProjectChange(); + } + listenToProjectChange() { reaction( () => this.projects.project, (project) => { From 4c1f5fd70c53aa54c9214d39f952d3fdd6edc52f Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Fri, 20 Sep 2024 16:07:10 -0400 Subject: [PATCH 02/40] Add wheel to carousel --- .../projects/ProjectsTab/Select/Carousel.tsx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/src/routes/projects/ProjectsTab/Select/Carousel.tsx b/app/src/routes/projects/ProjectsTab/Select/Carousel.tsx index 68dc7099e..83bd494b5 100644 --- a/app/src/routes/projects/ProjectsTab/Select/Carousel.tsx +++ b/app/src/routes/projects/ProjectsTab/Select/Carousel.tsx @@ -61,6 +61,22 @@ const EmblaCarousel: React.FC = ({ slides, onSlideChange }) }; }, [scrollPrev, scrollNext]); + const handleWheel = useCallback( + (e: React.WheelEvent) => { + e.preventDefault(); + const threshold = 50; // Adjust this value to change the sensitivity + + if (Math.abs(e.deltaY) > threshold) { + if (e.deltaY > 0) { + scrollNext(); + } else { + scrollPrev(); + } + } + }, + [scrollNext, scrollPrev], + ); + return (
= ({ slides, onSlideChange }) zIndex: -1, }} > -
+
{slides.map((slide, index) => (
Date: Fri, 20 Sep 2024 16:12:31 -0400 Subject: [PATCH 03/40] Button transition --- app/src/routes/projects/ProjectsTab/Select/Carousel.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/routes/projects/ProjectsTab/Select/Carousel.tsx b/app/src/routes/projects/ProjectsTab/Select/Carousel.tsx index 83bd494b5..de6f33aea 100644 --- a/app/src/routes/projects/ProjectsTab/Select/Carousel.tsx +++ b/app/src/routes/projects/ProjectsTab/Select/Carousel.tsx @@ -121,7 +121,7 @@ const EmblaCarousel: React.FC = ({ slides, onSlideChange }) disabled={!prevBtnEnabled} >
@@ -135,7 +135,7 @@ const EmblaCarousel: React.FC = ({ slides, onSlideChange }) disabled={!nextBtnEnabled} >
From 15720517d9baa55b80127a69374165e7527d2e74 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Fri, 20 Sep 2024 16:21:37 -0400 Subject: [PATCH 04/40] Create folder from project name --- .../ProjectsTab/Create/New/SelectFolder.tsx | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/app/src/routes/projects/ProjectsTab/Create/New/SelectFolder.tsx b/app/src/routes/projects/ProjectsTab/Create/New/SelectFolder.tsx index a26cf0768..c1ee0e569 100644 --- a/app/src/routes/projects/ProjectsTab/Create/New/SelectFolder.tsx +++ b/app/src/routes/projects/ProjectsTab/Create/New/SelectFolder.tsx @@ -24,17 +24,35 @@ export const NewSelectFolder = ({ if (path == null) { return; } + + const pathWithProject = `${path}/${nameToFolderName(projectData.name || 'new-project')}`; + setProjectData({ + ...projectData, + folderPath: pathWithProject, + }); + } + + function nameToFolderName(name: string): string { + return name + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') // Replace non-alphanumeric characters with hyphens + .replace(/^-+|-+$/g, '') // Remove leading and trailing hyphens + .replace(/^(\d)/, '_$1'); // Prepend underscore if the name starts with a digit + } + + function goBack() { setProjectData({ ...projectData, - folderPath: path, + folderPath: undefined, }); + prevStep(); } return ( {'Select your project folder'} - {'This is where we’ll reference your App'} + {"We'll a folder with your new app here"} {projectData.folderPath ? ( @@ -70,7 +88,7 @@ export const NewSelectFolder = ({

{`${currentStep + 1} of ${totalSteps}`}

-
diff --git a/utils/src/create/index.ts b/utils/src/create/index.ts index 3fdf4b087..696497ae7 100644 --- a/utils/src/create/index.ts +++ b/utils/src/create/index.ts @@ -23,7 +23,7 @@ export async function createProject( try { // Clone the template using degit - onProgress(ProjectCreationStage.CLONING, `Cloning template to ${fullPath}`); + onProgress(ProjectCreationStage.CLONING, `Cloning template...`); const emitter = degit(NEXT_TEMPLATE_REPO, { cache: false, force: true, @@ -36,10 +36,10 @@ export async function createProject( process.chdir(fullPath); // Install dependencies - onProgress(ProjectCreationStage.INSTALLING, 'Installing dependencies'); + onProgress(ProjectCreationStage.INSTALLING, 'Installing dependencies...'); await execAsync('npm install'); - onProgress(ProjectCreationStage.COMPLETE, 'Project created successfully'); + onProgress(ProjectCreationStage.COMPLETE, 'Project created successfully!'); } catch (error) { onProgress(ProjectCreationStage.ERROR, `Project creation failed: ${error}`); throw error; diff --git a/utils/src/models.ts b/utils/src/models.ts index 910db9c9a..2feb3a51d 100644 --- a/utils/src/models.ts +++ b/utils/src/models.ts @@ -1,18 +1,3 @@ -export type ApiResponse = SuccessResponse | ErrorResponse; - -export type SuccessResponse = { - status: 'success'; - data?: T; -}; - -export type ErrorResponse = { - status: 'error'; - error: { - message: string; - code: string; - }; -}; - export enum ProjectCreationStage { CLONING = 'cloning', INSTALLING = 'installing', From 1ea4be8a38d39a244c8dba1c32143d2f5a626e5c Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sat, 21 Sep 2024 13:25:19 -0400 Subject: [PATCH 18/40] Clean up --- app/electron/main/events/create.ts | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/app/electron/main/events/create.ts b/app/electron/main/events/create.ts index 1bdd71866..aca8d48c0 100644 --- a/app/electron/main/events/create.ts +++ b/app/electron/main/events/create.ts @@ -5,9 +5,6 @@ import { MainChannels } from '/common/constants'; export function listenForCreateMessages() { ipcMain.handle(MainChannels.CREATE_NEW_PROJECT, (e: Electron.IpcMainInvokeEvent, args) => { - console.log( - `[createProject] Received request to create project with args: ${JSON.stringify(args)}`, - ); const progressCallback: ProgressCallback = ( stage: ProjectCreationStage, message: string, @@ -19,28 +16,6 @@ export function listenForCreateMessages() { }; const { name, path } = args as { name: string; path: string }; - - // setTimeout(() => { - // mainWindow?.webContents.send(MainChannels.CREATE_NEW_PROJECT_CALLBACK, { - // stage: ProjectCreationStage.CLONING, - // message: 'Cloning template...', - // }); - // }, 1000); - - // setTimeout(() => { - // mainWindow?.webContents.send(MainChannels.CREATE_NEW_PROJECT_CALLBACK, { - // stage: ProjectCreationStage.INSTALLING, - // message: 'Installing...', - // }); - // }, 3000); - - // setTimeout(() => { - // mainWindow?.webContents.send(MainChannels.CREATE_NEW_PROJECT_CALLBACK, { - // stage: ProjectCreationStage.COMPLETE, - // message: 'Complete!', - // }); - // }, 5000); - // return; return createProject(name, path, progressCallback); }); } From a8277d991fdfac31efb4c96ea4d3e96283ca8183 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sat, 21 Sep 2024 13:39:09 -0400 Subject: [PATCH 19/40] Better wheel carousel --- .../projects/ProjectsTab/Select/Carousel.tsx | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/app/src/routes/projects/ProjectsTab/Select/Carousel.tsx b/app/src/routes/projects/ProjectsTab/Select/Carousel.tsx index c9b0beeac..b72f97f1c 100644 --- a/app/src/routes/projects/ProjectsTab/Select/Carousel.tsx +++ b/app/src/routes/projects/ProjectsTab/Select/Carousel.tsx @@ -1,6 +1,7 @@ import { ChevronDownIcon, ChevronUpIcon } from '@radix-ui/react-icons'; import useEmblaCarousel from 'embla-carousel-react'; -import React, { useCallback, useEffect, useState } from 'react'; +import debounce from 'lodash/debounce'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Project } from '/common/models/project'; interface EmblaCarouselProps { @@ -9,6 +10,7 @@ interface EmblaCarouselProps { } const EmblaCarousel: React.FC = ({ slides, onSlideChange }) => { + const WHEEL_SENSITIVITY = 10; const [emblaRef, emblaApi] = useEmblaCarousel({ axis: 'y', loop: false, @@ -55,26 +57,33 @@ const EmblaCarousel: React.FC = ({ slides, onSlideChange }) }; window.addEventListener('keydown', handleKeyDown); - - return () => { - window.removeEventListener('keydown', handleKeyDown); - }; + return () => window.removeEventListener('keydown', handleKeyDown); }, [scrollPrev, scrollNext]); + const debouncedScroll = useMemo( + () => + debounce( + (deltaY: number) => { + if (deltaY > 0) { + scrollNext(); + } else { + scrollPrev(); + } + }, + 50, + { leading: true, trailing: false }, + ), + [scrollNext, scrollPrev], + ); + const handleWheel = useCallback( (e: React.WheelEvent) => { e.preventDefault(); - const threshold = 10; // Adjust this value to change the sensitivity - - if (Math.abs(e.deltaY) > threshold) { - if (e.deltaY > 0) { - scrollNext(); - } else { - scrollPrev(); - } + if (Math.abs(e.deltaY) > WHEEL_SENSITIVITY) { + debouncedScroll(e.deltaY); } }, - [scrollNext, scrollPrev], + [debouncedScroll], ); return ( @@ -91,7 +100,7 @@ const EmblaCarousel: React.FC = ({ slides, onSlideChange }) }} >
- {slides.map((slide, index) => ( + {slides.map((slide) => (
Date: Sat, 21 Sep 2024 14:03:57 -0400 Subject: [PATCH 20/40] Add verifying step --- .../projects/ProjectsTab/Create/Load/Name.tsx | 62 ++++++++++++++++ .../ProjectsTab/Create/Load/Verify.tsx | 74 +++++++++++++------ .../ProjectsTab/Create/{ => New}/Name.tsx | 4 +- .../projects/ProjectsTab/Create/New/Setup.tsx | 4 + .../projects/ProjectsTab/Create/index.tsx | 7 +- 5 files changed, 122 insertions(+), 29 deletions(-) create mode 100644 app/src/routes/projects/ProjectsTab/Create/Load/Name.tsx rename app/src/routes/projects/ProjectsTab/Create/{ => New}/Name.tsx (96%) diff --git a/app/src/routes/projects/ProjectsTab/Create/Load/Name.tsx b/app/src/routes/projects/ProjectsTab/Create/Load/Name.tsx new file mode 100644 index 000000000..d6fe45ae5 --- /dev/null +++ b/app/src/routes/projects/ProjectsTab/Create/Load/Name.tsx @@ -0,0 +1,62 @@ +import { Button } from '@/components/ui/button'; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from '@/components/ui/card'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { StepProps } from '..'; + +export const LoadNameProject = ({ + props: { projectData, currentStep, setProjectData, totalSteps, prevStep, nextStep }, +}: { + props: StepProps; +}) => { + function setProjectName(name: string) { + setProjectData({ + ...projectData, + name, + }); + } + return ( + + + {'Let’s name your project'} + + {"We'll install the necessary dependencies for you"} + + + +
+ + setProjectName(e.currentTarget.value)} + /> +
+
+ +

{`${currentStep + 1} of ${totalSteps}`}

+
+ + +
+
+
+ ); +}; diff --git a/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx b/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx index cc5263a3c..1469626c6 100644 --- a/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx +++ b/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx @@ -7,9 +7,9 @@ import { CardHeader, CardTitle, } from '@/components/ui/card'; -import { CheckCircledIcon, ExclamationTriangleIcon } from '@radix-ui/react-icons'; +import { CheckCircledIcon, ExclamationTriangleIcon, ShadowIcon } from '@radix-ui/react-icons'; import clsx from 'clsx'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { StepProps } from '..'; export const LoadVerifyProject = ({ @@ -17,8 +17,16 @@ export const LoadVerifyProject = ({ }: { props: StepProps; }) => { + const [isVerifying, setIsVerifying] = useState(true); const [isInstalled, setIsInstalled] = useState(null); + useEffect(() => { + if (isVerifying) { + setTimeout(() => { + setIsInstalled(true); + }, 1000); + } + }, []); async function installOnlook() { setIsInstalled(true); } @@ -27,33 +35,46 @@ export const LoadVerifyProject = ({ - {isInstalled ? 'Onlook is installed' : 'Onlook is not installed'} + {isVerifying + ? 'Verifying project...' + : isInstalled + ? 'Onlook is installed' + : 'Onlook is not installed'} - {isInstalled - ? 'Your project is all set up' - : 'It takes one second to install Onlook on your project'} + {isVerifying + ? 'Checking your dependencies and configurations' + : isInstalled + ? 'Your project is all set up' + : 'It takes one second to install Onlook on your project'} -
-
-

{projectData.name}

-

{projectData.folderPath}

+ {isVerifying ? ( +
+ +

Rummaging...

- {isInstalled ? ( - - ) : ( - - )} -
+ ) : ( +
+
+

{projectData.name}

+

{projectData.folderPath}

+
+ {isInstalled ? ( + + ) : ( + + )} +
+ )}

{`${currentStep + 1} of ${totalSteps}`}

@@ -61,7 +82,12 @@ export const LoadVerifyProject = ({ - {isInstalled ? ( + + {isVerifying ? ( + + ) : isInstalled ? ( diff --git a/app/src/routes/projects/ProjectsTab/Create/Name.tsx b/app/src/routes/projects/ProjectsTab/Create/New/Name.tsx similarity index 96% rename from app/src/routes/projects/ProjectsTab/Create/Name.tsx rename to app/src/routes/projects/ProjectsTab/Create/New/Name.tsx index a97846101..7332a9974 100644 --- a/app/src/routes/projects/ProjectsTab/Create/Name.tsx +++ b/app/src/routes/projects/ProjectsTab/Create/New/Name.tsx @@ -9,9 +9,9 @@ import { } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; -import { StepProps } from '.'; +import { StepProps } from '..'; -export const NameProjectStep = ({ +export const NewNameProject = ({ props: { projectData, currentStep, setProjectData, totalSteps, prevStep, nextStep }, }: { props: StepProps; diff --git a/app/src/routes/projects/ProjectsTab/Create/New/Setup.tsx b/app/src/routes/projects/ProjectsTab/Create/New/Setup.tsx index ec3b7b85c..c778d4ab4 100644 --- a/app/src/routes/projects/ProjectsTab/Create/New/Setup.tsx +++ b/app/src/routes/projects/ProjectsTab/Create/New/Setup.tsx @@ -38,6 +38,10 @@ export const NewSetupProject = ({ } }, ); + + return () => { + window.api.removeAllListeners(MainChannels.CREATE_NEW_PROJECT_CALLBACK); + }; }, []); function handleClickPath() { diff --git a/app/src/routes/projects/ProjectsTab/Create/index.tsx b/app/src/routes/projects/ProjectsTab/Create/index.tsx index e81820220..1c1395435 100644 --- a/app/src/routes/projects/ProjectsTab/Create/index.tsx +++ b/app/src/routes/projects/ProjectsTab/Create/index.tsx @@ -1,10 +1,11 @@ import { useProjectsManager } from '@/components/Context'; import { useEffect, useState } from 'react'; import { CreateMethod } from '../..'; +import { LoadNameProject } from './Load/Name'; import { LoadSelectFolder } from './Load/SelectFolder'; import { LoadSetUrl } from './Load/SetUrl'; import { LoadVerifyProject } from './Load/Verify'; -import { NameProjectStep } from './Name'; +import { NewNameProject } from './New/Name'; import { NewRunProject } from './New/Run'; import { NewSelectFolder } from './New/SelectFolder'; import { NewSetupProject } from './New/Setup'; @@ -84,7 +85,7 @@ const CreateProject = ({ return ; } if (currentStep === 1) { - return ; + return ; } if (currentStep === 2) { return ; @@ -94,7 +95,7 @@ const CreateProject = ({ } } else if (createMethod === CreateMethod.NEW) { if (currentStep === 0) { - return ; + return ; } if (currentStep === 1) { return ; From 8e4b561afcdbf3b74909e04ad15d75c979e22553 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sat, 21 Sep 2024 14:09:37 -0400 Subject: [PATCH 21/40] Update CRA for utils --- app/electron/main/events/create.ts | 5 +++++ utils/src/setup/cra.ts | 5 +++++ utils/src/setup/frameworks.ts | 8 +++++--- utils/src/setup/index.ts | 32 ++++++++++-------------------- utils/src/verify/index.ts | 0 5 files changed, 25 insertions(+), 25 deletions(-) create mode 100644 utils/src/verify/index.ts diff --git a/app/electron/main/events/create.ts b/app/electron/main/events/create.ts index aca8d48c0..34df27e23 100644 --- a/app/electron/main/events/create.ts +++ b/app/electron/main/events/create.ts @@ -18,4 +18,9 @@ export function listenForCreateMessages() { const { name, path } = args as { name: string; path: string }; return createProject(name, path, progressCallback); }); + + ipcMain.handle( + MainChannels.VERIFY_PROJECT, + (e: Electron.IpcMainInvokeEvent, args: string) => {}, + ); } diff --git a/utils/src/setup/cra.ts b/utils/src/setup/cra.ts index 3955f52ad..a734888d4 100644 --- a/utils/src/setup/cra.ts +++ b/utils/src/setup/cra.ts @@ -24,6 +24,11 @@ const defaultContent = ` ); `; +export const modifyCRAConfig = (): void => { + ensureConfigOverrides(); + modifyStartScript(); +} + export const ensureConfigOverrides = (): void => { // Handle the case when the file does not exist if (!fs.existsSync(configOverridesPath)) { diff --git a/utils/src/setup/frameworks.ts b/utils/src/setup/frameworks.ts index 8885b5993..c5fb70fc8 100644 --- a/utils/src/setup/frameworks.ts +++ b/utils/src/setup/frameworks.ts @@ -1,14 +1,16 @@ -import { BUILD_TOOL_NAME, CONFIG_FILE_PATTERN, NEXT_DEPENDENCIES, VITE_DEPENDENCIES, WEBPACK_DEPENDENCIES } from "./constants"; +import { BUILD_TOOL_NAME, CONFIG_FILE_PATTERN, CRA_DEPENDENCIES, NEXT_DEPENDENCIES, VITE_DEPENDENCIES, WEBPACK_DEPENDENCIES } from "./constants"; +import { isCRAProject, modifyCRAConfig } from "./cra"; import { isNextJsProject, modifyNextConfig } from "./next"; import { getFileExtensionByPattern, installPackages } from "./utils"; import { isViteJsProject, modifyViteConfig } from "./vite"; import { isWebpackProject, modifyWebpackConfig } from "./webpack"; + export class Framework { static readonly NEXT = new Framework("Next.js", isNextJsProject, modifyNextConfig, NEXT_DEPENDENCIES, BUILD_TOOL_NAME.NEXT); static readonly VITE = new Framework("Vite", isViteJsProject, modifyViteConfig, VITE_DEPENDENCIES, BUILD_TOOL_NAME.VITE); static readonly WEBPACK = new Framework("Webpack", isWebpackProject, modifyWebpackConfig, WEBPACK_DEPENDENCIES, BUILD_TOOL_NAME.WEBPACK); - // static readonly CRA = new Framework("Create React App", isCRAProject, modifyCRAConfig, CRA_DEPENDENCIES, BUILD_TOOL_NAME.CRA); + static readonly CRA = new Framework("Create React App", isCRAProject, modifyCRAConfig, CRA_DEPENDENCIES, BUILD_TOOL_NAME.CRA); private constructor( public readonly name: string, @@ -38,7 +40,7 @@ export class Framework { this.NEXT, this.VITE, this.WEBPACK, - // this.CRA, + this.CRA, ]; } } diff --git a/utils/src/setup/index.ts b/utils/src/setup/index.ts index 1440b6c70..0aad3739d 100755 --- a/utils/src/setup/index.ts +++ b/utils/src/setup/index.ts @@ -1,28 +1,16 @@ -import { CRA_DEPENDENCIES } from './constants'; -import { ensureConfigOverrides, isCRAProject, modifyStartScript } from './cra'; import { Framework } from './frameworks'; -import { installPackages } from './utils'; export const setup = async (): Promise => { - try { - for (const framework of Framework.getAll()) { - const updated = await framework.run(); - if (updated) { - return; - } - } + try { + for (const framework of Framework.getAll()) { + const updated = await framework.run(); + if (updated) { + return; + } + } - if (await isCRAProject()) { - console.log('This is a create-react-app project.'); - await installPackages(CRA_DEPENDENCIES); - ensureConfigOverrides(); - modifyStartScript(); - return; + console.warn('Cannot determine the project framework.', '\nIf this is unexpected, see: https://github.com/onlook-dev/onlook/wiki/How-to-set-up-my-project%3F#do-it-manually'); + } catch (err) { + console.error(err); } - - - console.warn('Cannot determine the project framework.', '\nIf this is unexpected, see: https://github.com/onlook-dev/onlook/wiki/How-to-set-up-my-project%3F#do-it-manually'); - } catch (err) { - console.error(err); - } }; diff --git a/utils/src/verify/index.ts b/utils/src/verify/index.ts new file mode 100644 index 000000000..e69de29bb From c482222dcbef55636061f3ccecda849f308867c5 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sat, 21 Sep 2024 14:21:30 -0400 Subject: [PATCH 22/40] Add setup --- cli/src/create/index.ts | 12 +- cli/src/setup/constants.ts | 66 ----------- cli/src/setup/cra.ts | 165 -------------------------- cli/src/setup/frameworks.ts | 44 ------- cli/src/setup/index.ts | 51 ++++---- cli/src/setup/next.ts | 184 ----------------------------- cli/src/setup/utils.ts | 133 --------------------- cli/src/setup/vite.ts | 224 ------------------------------------ cli/src/setup/webpack.ts | 168 --------------------------- utils/src/create/index.ts | 13 +-- utils/src/index.ts | 26 ++++- utils/src/models.ts | 8 -- utils/src/setup/index.ts | 4 +- 13 files changed, 69 insertions(+), 1029 deletions(-) delete mode 100644 cli/src/setup/constants.ts delete mode 100644 cli/src/setup/cra.ts delete mode 100644 cli/src/setup/frameworks.ts delete mode 100644 cli/src/setup/next.ts delete mode 100644 cli/src/setup/utils.ts delete mode 100644 cli/src/setup/vite.ts delete mode 100644 cli/src/setup/webpack.ts delete mode 100644 utils/src/models.ts diff --git a/cli/src/create/index.ts b/cli/src/create/index.ts index 15cb7a55a..cb6c958b2 100644 --- a/cli/src/create/index.ts +++ b/cli/src/create/index.ts @@ -1,4 +1,4 @@ -import { createProject, ProjectCreationStage, type ProgressCallback } from '@onlook/utils'; +import { createProject, CreateStage, type CreateCallback } from '@onlook/utils'; import ora from 'ora'; export async function create(projectName: string): Promise { @@ -6,21 +6,21 @@ export async function create(projectName: string): Promise { const targetPath = process.cwd(); const spinner = ora('Initializing project...').start(); - const progressCallback: ProgressCallback = (stage, message) => { + const progressCallback: CreateCallback = (stage: CreateStage, message: string) => { switch (stage) { - case ProjectCreationStage.CLONING: + case CreateStage.CLONING: spinner.text = 'Cloning template...'; break; - case ProjectCreationStage.INSTALLING: + case CreateStage.INSTALLING: spinner.text = 'Installing dependencies...'; break; - case ProjectCreationStage.COMPLETE: + case CreateStage.COMPLETE: spinner.succeed('Project created successfully!'); console.log('\nTo get started:'); console.log(` cd ${projectName}`); console.log(' npm run dev'); break; - case ProjectCreationStage.ERROR: + case CreateStage.ERROR: spinner.fail(message); break; } diff --git a/cli/src/setup/constants.ts b/cli/src/setup/constants.ts deleted file mode 100644 index 4d889a2d7..000000000 --- a/cli/src/setup/constants.ts +++ /dev/null @@ -1,66 +0,0 @@ -export enum BUILD_TOOL_NAME { - NEXT = "next", - WEBPACK = "webpack", - CRA = "cra", - VITE = "vite", -} - -export enum DEPENDENCY_NAME { - NEXT = "next", - WEBPACK = "webpack", - CRA = "react-scripts", - VITE = "vite" -} - -export const NEXTJS_CONFIG_BASE_NAME = 'next.config'; -export const WEBPACK_CONFIG_BASE_NAME = 'webpack.config'; -export const VITEJS_CONFIG_BASE_NAME = 'vite.config'; - -export const CONFIG_FILE_PATTERN: Record = { - [BUILD_TOOL_NAME.NEXT]: `${NEXTJS_CONFIG_BASE_NAME}.*`, - [BUILD_TOOL_NAME.WEBPACK]: `${WEBPACK_CONFIG_BASE_NAME}.*`, - [BUILD_TOOL_NAME.VITE]: `${VITEJS_CONFIG_BASE_NAME}.*`, - [BUILD_TOOL_NAME.CRA]: '' -} - -export const PACKAGE_JSON = 'package.json'; - -export enum LOCK_FILE_NAME { - YARN = 'yarn.lock', - BUN = 'bun.lockb', - PNPM = 'pnpm-lock.yaml' -} - -export enum PACKAGE_MANAGER { - YARN = 'yarn', - NPM = 'npm', - PNPM = 'pnpm', - BUN = 'bun' -} - -export const ONLOOK_NEXTJS_PLUGIN = '@onlook/nextjs'; -export const ONLOOK_WEBPACK_PLUGIN = '@onlook/react'; -export const ONLOOK_BABEL_PLUGIN = '@onlook/babel-plugin-react'; - -export const NEXTJS_COMMON_FILES = ['pages', 'app', 'src/pages', 'src/app']; -export const CRA_COMMON_FILES = ['public', 'src']; - -export const CONFIG_OVERRIDES_FILE = 'config-overrides.js'; -export const BABELRC_FILE = '.babelrc'; - -export const JS_FILE_EXTENSION = '.js'; -export const MJS_FILE_EXTENSION = '.mjs'; -export const TS_FILE_EXTENSION = '.ts'; - -export const NEXT_DEPENDENCIES = [ONLOOK_NEXTJS_PLUGIN]; -export const CRA_DEPENDENCIES = [ONLOOK_BABEL_PLUGIN, 'customize-cra', 'react-app-rewired']; -export const VITE_DEPENDENCIES = [ONLOOK_BABEL_PLUGIN]; - -export const WEBPACK_DEPENDENCIES = [ - ONLOOK_BABEL_PLUGIN, - 'babel-loader', - '@babel/preset-react', - '@babel/core', - '@babel/preset-env', - 'webpack' -]; diff --git a/cli/src/setup/cra.ts b/cli/src/setup/cra.ts deleted file mode 100644 index 3955f52ad..000000000 --- a/cli/src/setup/cra.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { CONFIG_OVERRIDES_FILE, CRA_COMMON_FILES, DEPENDENCY_NAME, JS_FILE_EXTENSION, ONLOOK_WEBPACK_PLUGIN, PACKAGE_JSON } from "./constants"; -import { exists, genASTParserOptionsByFileExtension, hasDependency } from "./utils"; - -import generate from '@babel/generator'; -import { parse } from '@babel/parser'; -import traverse from '@babel/traverse'; -import * as t from '@babel/types'; -import * as fs from 'fs'; -import * as path from 'path'; - -const configOverridesPath = path.resolve(CONFIG_OVERRIDES_FILE); -const packageJsonPath = path.resolve(PACKAGE_JSON); - -const requiredImportSource = 'customize-cra'; - -// Define the content to be added if the file does not exist -const defaultContent = ` - const { override, addBabelPlugins } = require('${requiredImportSource}'); - - module.exports = override( - ...addBabelPlugins( - '${ONLOOK_WEBPACK_PLUGIN}' - ) - ); -`; - -export const ensureConfigOverrides = (): void => { - // Handle the case when the file does not exist - if (!fs.existsSync(configOverridesPath)) { - fs.writeFileSync(configOverridesPath, defaultContent, 'utf8'); - console.log(`${CONFIG_OVERRIDES_FILE} has been created with the necessary config.`); - return; - } - - // Handle the case when the file exists - fs.readFile(configOverridesPath, 'utf8', (err, fileContent) => { - if (err) { - console.error(`Error reading ${configOverridesPath}:`, err); - return; - } - // Read the existing file - const ast = parse(fileContent, genASTParserOptionsByFileExtension(JS_FILE_EXTENSION)); - - let hasCustomizeCraImport = false; - let hasOnlookReactPlugin = false; - - traverse(ast, { - ImportDeclaration(path) { - if (path.node.source.value === requiredImportSource) { - hasCustomizeCraImport = true; - } - }, - VariableDeclarator(path) { - if (t.isCallExpression(path.node.init) && - t.isIdentifier(path.node.init.callee, { name: 'require' }) && - path.node.init.arguments.length === 1 && - t.isStringLiteral(path.node.init.arguments[0], { value: requiredImportSource })) { - hasCustomizeCraImport = true; - } - }, - CallExpression(path) { - if (t.isIdentifier(path.node.callee, { name: 'override' })) { - path.node.arguments.forEach(arg => { - if (t.isSpreadElement(arg) && t.isCallExpression(arg.argument) && - t.isIdentifier(arg.argument.callee, { name: 'addBabelPlugins' }) && - arg.argument.arguments.some(pluginArg => t.isStringLiteral(pluginArg, { value: ONLOOK_WEBPACK_PLUGIN }))) { - hasOnlookReactPlugin = true; - } - }); - } - } - }); - - if (!hasCustomizeCraImport) { - const requireDeclaration = t.variableDeclaration('const', [ - t.variableDeclarator( - t.objectPattern([ - t.objectProperty(t.identifier('override'), t.identifier('override')), - t.objectProperty(t.identifier('addBabelPlugins'), t.identifier('addBabelPlugins')) - ]), - t.callExpression(t.identifier('require'), [t.stringLiteral(requiredImportSource)]) - ) - ]); - ast.program.body.unshift(requireDeclaration); - } - - if (!hasOnlookReactPlugin) { - traverse(ast, { - AssignmentExpression(path) { - if (t.isMemberExpression(path.node.left) && - t.isIdentifier(path.node.left.object, { name: 'module' }) && - t.isIdentifier(path.node.left.property, { name: 'exports' })) { - // @ts-ignore - path.node.right.arguments.push( - t.spreadElement(t.callExpression(t.identifier('addBabelPlugins'), [ - t.stringLiteral(ONLOOK_WEBPACK_PLUGIN) - ])) - ); - } - } - }); - } - - const updatedCode = generate(ast, {}, fileContent).code; - // Write the updated content back to next.config.* file - fs.writeFile(configOverridesPath, updatedCode, 'utf8', (err) => { - if (err) { - console.error(`Error writing ${configOverridesPath}:`, err); - return; - } - - console.log(`Successfully updated ${configOverridesPath}`); - }); - }); -}; - -export const isCRAProject = async (): Promise => { - try { - // Check if the dependency exists - if (!await hasDependency(DEPENDENCY_NAME.CRA)) { - return false; - } - - // Check if one of the directories exists - const directoryExists = await Promise.all(CRA_COMMON_FILES.map(exists)); - - return directoryExists.some(Boolean); - } catch (err) { - console.error(err); - return false; - } -}; - -export const modifyStartScript = (): void => { - fs.readFile(packageJsonPath, 'utf8', (err, fileContent) => { - if (err) { - console.error('Error reading package.json:', err); - return; - } - - const packageJSON = JSON.parse(fileContent); - if (!packageJSON.scripts) { - packageJSON.scripts = {}; - } - - const scriptsToUpdate = ['start', 'test', 'build']; - - scriptsToUpdate.forEach(script => { - if (!packageJSON.scripts[script]) { - packageJSON.scripts[script] = `react-app-rewired ${script}`; - } else { - packageJSON.scripts[script] = packageJSON.scripts[script].replace(/react-scripts/, 'react-app-rewired'); - } - }); - - fs.writeFile('package.json', JSON.stringify(packageJSON, null, 2), 'utf8', (err) => { - if (err) { - console.error('Error writing package.json:', err); - return; - } - - console.log('Successfully updated the start script in package.json'); - }); - }); -}; diff --git a/cli/src/setup/frameworks.ts b/cli/src/setup/frameworks.ts deleted file mode 100644 index 8885b5993..000000000 --- a/cli/src/setup/frameworks.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { BUILD_TOOL_NAME, CONFIG_FILE_PATTERN, NEXT_DEPENDENCIES, VITE_DEPENDENCIES, WEBPACK_DEPENDENCIES } from "./constants"; -import { isNextJsProject, modifyNextConfig } from "./next"; -import { getFileExtensionByPattern, installPackages } from "./utils"; -import { isViteJsProject, modifyViteConfig } from "./vite"; -import { isWebpackProject, modifyWebpackConfig } from "./webpack"; - -export class Framework { - static readonly NEXT = new Framework("Next.js", isNextJsProject, modifyNextConfig, NEXT_DEPENDENCIES, BUILD_TOOL_NAME.NEXT); - static readonly VITE = new Framework("Vite", isViteJsProject, modifyViteConfig, VITE_DEPENDENCIES, BUILD_TOOL_NAME.VITE); - static readonly WEBPACK = new Framework("Webpack", isWebpackProject, modifyWebpackConfig, WEBPACK_DEPENDENCIES, BUILD_TOOL_NAME.WEBPACK); - // static readonly CRA = new Framework("Create React App", isCRAProject, modifyCRAConfig, CRA_DEPENDENCIES, BUILD_TOOL_NAME.CRA); - - private constructor( - public readonly name: string, - public readonly identify: () => Promise, - public readonly updateConfig: (configFileExtension: string) => void, - public readonly dependencies: string[], - public readonly buildToolName: BUILD_TOOL_NAME - ) { } - - run = async (): Promise => { - if (await this.identify()) { - console.log(`This is a ${this.name} project.`); - - await installPackages(this.dependencies); - - const configFileExtension = await getFileExtensionByPattern(process.cwd(), CONFIG_FILE_PATTERN[this.buildToolName]); - if (configFileExtension) { - await this.updateConfig(configFileExtension); - } - return true; - } - return false; - } - - static getAll(): Framework[] { - return [ - this.NEXT, - this.VITE, - this.WEBPACK, - // this.CRA, - ]; - } -} diff --git a/cli/src/setup/index.ts b/cli/src/setup/index.ts index 1440b6c70..f097b6cb9 100755 --- a/cli/src/setup/index.ts +++ b/cli/src/setup/index.ts @@ -1,28 +1,37 @@ -import { CRA_DEPENDENCIES } from './constants'; -import { ensureConfigOverrides, isCRAProject, modifyStartScript } from './cra'; -import { Framework } from './frameworks'; -import { installPackages } from './utils'; +import { type SetupCallback, setupProject, SetupStage } from '@onlook/utils'; +import ora from 'ora'; export const setup = async (): Promise => { - try { - for (const framework of Framework.getAll()) { - const updated = await framework.run(); - if (updated) { - return; - } + const targetPath = process.cwd(); + const spinner = ora('Initializing project...').start(); + + const progressCallback: SetupCallback = (stage: SetupStage, message: string) => { + switch (stage) { + case SetupStage.INSTALLING: + spinner.text = 'Cloning template...'; + break; + case SetupStage.CONFIGURING: + spinner.text = 'Installing dependencies...'; + break; + case SetupStage.COMPLETE: + spinner.succeed('Project created successfully!'); + console.log('\nTo get started:'); + console.log(` cd ${targetPath}`); + console.log(' npm run dev'); + break; + case SetupStage.ERROR: + spinner.fail(message); + break; + } } - if (await isCRAProject()) { - console.log('This is a create-react-app project.'); - await installPackages(CRA_DEPENDENCIES); - ensureConfigOverrides(); - modifyStartScript(); - return; + try { + await setupProject(targetPath, progressCallback); + } catch (error) { + spinner.fail('An error occurred'); + console.error('Error details:', error); + process.exit(1); } +}; - console.warn('Cannot determine the project framework.', '\nIf this is unexpected, see: https://github.com/onlook-dev/onlook/wiki/How-to-set-up-my-project%3F#do-it-manually'); - } catch (err) { - console.error(err); - } -}; diff --git a/cli/src/setup/next.ts b/cli/src/setup/next.ts deleted file mode 100644 index 922212219..000000000 --- a/cli/src/setup/next.ts +++ /dev/null @@ -1,184 +0,0 @@ -#!/usr/bin/env node -import * as fs from 'fs'; -import * as path from 'path'; - -import generate from '@babel/generator'; -import { parse } from '@babel/parser'; -import traverse from '@babel/traverse'; -import * as t from '@babel/types'; - -import { - checkVariableDeclarationExist, - exists, - genASTParserOptionsByFileExtension, - genImportDeclaration, - hasDependency, - isSupportFileExtension -} from './utils'; - -import { - BUILD_TOOL_NAME, - CONFIG_FILE_PATTERN, - DEPENDENCY_NAME, - NEXTJS_COMMON_FILES, - NEXTJS_CONFIG_BASE_NAME, - ONLOOK_NEXTJS_PLUGIN, -} from './constants'; - -export const isNextJsProject = async (): Promise => { - try { - const configPath = CONFIG_FILE_PATTERN[BUILD_TOOL_NAME.NEXT]; - - // Check if the configuration file exists - if (await exists(configPath)) { - return true; - } - - // Check if the dependency exists - if (!await hasDependency(DEPENDENCY_NAME.NEXT)) { - return false; - } - - // Check if one of the directories exists - const directoryExists = await Promise.all(NEXTJS_COMMON_FILES.map(exists)); - - return directoryExists.some(Boolean); - } catch (err) { - console.error(err); - return false; - } -}; - -export const modifyNextConfig = (configFileExtension: string): void => { - if (!isSupportFileExtension(configFileExtension)) { - console.error('Unsupported file extension'); - return; - } - - const configFileName = `${NEXTJS_CONFIG_BASE_NAME}${configFileExtension}`; - - // Define the path to next.config.* file - const configPath = path.resolve(process.cwd(), configFileName); - - if (!fs.existsSync(configPath)) { - console.error(`${configFileName} not found`); - return; - } - - console.log(`Adding ${ONLOOK_NEXTJS_PLUGIN} plugin into ${configFileName} file...`); - - // Read the existing next.config.* file - fs.readFile(configPath, 'utf8', (err, data) => { - if (err) { - console.error(`Error reading ${configPath}:`, err); - return; - } - - const astParserOption = genASTParserOptionsByFileExtension(configFileExtension); - - // Parse the file content to an AST - const ast = parse(data, astParserOption); - - let hasPathImport = false; - - // Traverse the AST to find the experimental.swcPlugins array - traverse(ast, { - VariableDeclarator(path) { - // check if path is imported in .js file - if (checkVariableDeclarationExist(path, 'path')) { - hasPathImport = true; - } - }, - ImportDeclaration(path) { - // check if path is imported in .mjs file - if (path.node.source.value === 'path') { - hasPathImport = true; - } - }, - ObjectExpression(path) { - const properties = path.node.properties; - let experimentalProperty: t.ObjectProperty | undefined; - - // Find the experimental property - properties.forEach(prop => { - if (t.isObjectProperty(prop) && t.isIdentifier(prop.key, { name: 'experimental' })) { - experimentalProperty = prop; - } - }); - - if (!experimentalProperty) { - // If experimental property is not found, create it - experimentalProperty = t.objectProperty( - t.identifier('experimental'), - t.objectExpression([]) - ); - properties.push(experimentalProperty); - } - - // Ensure experimental is an ObjectExpression - if (!t.isObjectExpression(experimentalProperty.value)) { - experimentalProperty.value = t.objectExpression([]); - } - - const experimentalProperties = experimentalProperty.value.properties; - let swcPluginsProperty: t.ObjectProperty | undefined; - - // Find the swcPlugins property - experimentalProperties.forEach(prop => { - if (t.isObjectProperty(prop) && t.isIdentifier(prop.key, { name: 'swcPlugins' })) { - swcPluginsProperty = prop; - } - }); - - if (!swcPluginsProperty) { - // If swcPlugins property is not found, create it - swcPluginsProperty = t.objectProperty( - t.identifier('swcPlugins'), - t.arrayExpression([]) - ); - experimentalProperties.push(swcPluginsProperty); - } - - // Ensure swcPlugins is an ArrayExpression - if (!t.isArrayExpression(swcPluginsProperty.value)) { - swcPluginsProperty.value = t.arrayExpression([]); - } - - // Add the new plugin configuration to swcPlugins array - const pluginConfig = t.arrayExpression([ - t.stringLiteral(ONLOOK_NEXTJS_PLUGIN), - t.objectExpression([ - t.objectProperty( - t.identifier('root'), - t.callExpression(t.memberExpression(t.identifier('path'), t.identifier('resolve')), [t.stringLiteral('.')]) - ) - ]) - ]); - - swcPluginsProperty.value.elements.push(pluginConfig); - - // Stop traversing after the modification - path.stop(); - } - }); - - // If 'path' is not imported, add the import statement - if (!hasPathImport) { - const importDeclaration = genImportDeclaration(configFileExtension, 'path'); - importDeclaration && ast.program.body.unshift(importDeclaration); - } - - // Generate the modified code from the AST - const updatedCode = generate(ast, {}, data).code; - - // Write the updated content back to next.config.* file - fs.writeFile(configPath, updatedCode, 'utf8', (err) => { - if (err) { - console.error(`Error writing ${configPath}:`, err); - return; - } - - console.log(`Successfully updated ${configPath}`); - }); - }); -}; diff --git a/cli/src/setup/utils.ts b/cli/src/setup/utils.ts deleted file mode 100644 index 7799af6df..000000000 --- a/cli/src/setup/utils.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { NodePath } from '@babel/traverse'; -import * as t from '@babel/types'; -import { execSync } from 'child_process'; -import * as glob from 'glob'; -import * as path from 'path'; -import { - JS_FILE_EXTENSION, - LOCK_FILE_NAME, - MJS_FILE_EXTENSION, - PACKAGE_JSON, - PACKAGE_MANAGER, - TS_FILE_EXTENSION -} from './constants'; - -export const exists = async (filePattern: string): Promise => { - try { - const pattern = path.resolve(process.cwd(), filePattern); - const files = getFileNamesByPattern(pattern); - return files.length > 0; - } catch (err) { - console.error(err); - return false; - } -}; - -export const getFileNamesByPattern = (pattern: string): string[] => glob.globSync(pattern); - -export const installPackages = async (packages: string[]): Promise => { - const packageManager = await getPackageManager(); - const command = packageManager === PACKAGE_MANAGER.YARN ? 'yarn add -D' : `${packageManager} install -D`; - - console.log("Package manager found:", packageManager) - console.log("\n$", `${command} ${packages.join(' ')}`) - - execSync(`${command} ${packages.join(' ')}`, { stdio: 'inherit' }); -}; - -export const getPackageManager = async (): Promise => { - try { - if (await exists(LOCK_FILE_NAME.YARN)) { - return PACKAGE_MANAGER.YARN; - } - if (await exists(LOCK_FILE_NAME.PNPM)) { - return PACKAGE_MANAGER.PNPM; - } - if (await exists(LOCK_FILE_NAME.BUN)) { - return PACKAGE_MANAGER.BUN; - } - return PACKAGE_MANAGER.NPM; - } catch (e) { - console.error("Error determining package manager, using npm by default", e) - return PACKAGE_MANAGER.NPM - } - -}; - -export const hasDependency = async (dependencyName: string): Promise => { - const packageJsonPath = path.resolve(PACKAGE_JSON); - if (await exists(packageJsonPath)) { - const packageJson = require(packageJsonPath); - return ( - (packageJson.dependencies && packageJson.dependencies[dependencyName]) || - (packageJson.devDependencies && packageJson.devDependencies[dependencyName]) - ); - } - return false; -}; - -export const getFileExtensionByPattern = async (dir: string, filePattern: string): Promise => { - const fullDirPattern = path.resolve(dir, filePattern); - const files = await getFileNamesByPattern(fullDirPattern); - - if (files.length > 0) { - return path.extname(files[0]); - } - - return null; -}; - -export const genASTParserOptionsByFileExtension = (fileExtension: string, sourceType: string = 'module'): object => { - switch (fileExtension) { - case JS_FILE_EXTENSION: - return { - sourceType: sourceType - }; - case MJS_FILE_EXTENSION: - return { - sourceType: sourceType, - plugins: ['jsx'] - }; - case TS_FILE_EXTENSION: - return { - sourceType: sourceType, - plugins: ['typescript'] - }; - default: - return {}; - } -}; - -export const genImportDeclaration = (fileExtension: string, dependency: string): t.VariableDeclaration | t.ImportDeclaration | null => { - switch (fileExtension) { - case JS_FILE_EXTENSION: - return t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier(dependency), - t.callExpression(t.identifier('require'), [t.stringLiteral(dependency)]) - ) - ]); - case MJS_FILE_EXTENSION: - return t.importDeclaration( - [t.importDefaultSpecifier(t.identifier(dependency))], - t.stringLiteral(dependency) - ); - default: - return null; - } -}; - -export const checkVariableDeclarationExist = (path: NodePath, dependency: string): boolean => { - return t.isIdentifier(path.node.id, { name: dependency }) && - t.isCallExpression(path.node.init) && - (path.node.init.callee as t.V8IntrinsicIdentifier).name === 'require' && - (path.node.init.arguments[0] as any).value === dependency; -}; - -export const isSupportFileExtension = (fileExtension: string): boolean => { - return [JS_FILE_EXTENSION, MJS_FILE_EXTENSION].indexOf(fileExtension) !== -1; -}; - -export const isViteProjectSupportFileExtension = (fileExtension: string): boolean => { - return [JS_FILE_EXTENSION, TS_FILE_EXTENSION].indexOf(fileExtension) !== -1; -}; diff --git a/cli/src/setup/vite.ts b/cli/src/setup/vite.ts deleted file mode 100644 index b6ca41d1b..000000000 --- a/cli/src/setup/vite.ts +++ /dev/null @@ -1,224 +0,0 @@ -import generate from '@babel/generator'; -import { parse } from '@babel/parser'; -import traverse from '@babel/traverse'; -import * as t from '@babel/types'; -import * as fs from 'fs'; -import * as path from 'path'; - -import { - BUILD_TOOL_NAME, - CONFIG_FILE_PATTERN, - DEPENDENCY_NAME, - ONLOOK_BABEL_PLUGIN, - VITEJS_CONFIG_BASE_NAME -} from "./constants"; -import { - exists, - genASTParserOptionsByFileExtension, - hasDependency, - isViteProjectSupportFileExtension -} from "./utils"; - -// Function to check if a plugin is already in the array -function hasPlugin(pluginsArray: t.Expression[], pluginName: string): boolean { - return pluginsArray.some( - (plugin) => - (t.isStringLiteral(plugin) && plugin.value === pluginName) || - (t.isIdentifier(plugin) && plugin.name === pluginName) || - (t.isCallExpression(plugin) && t.isIdentifier(plugin.callee) && plugin.callee.name === pluginName) - ); -} - -export const isViteJsProject = async (): Promise => { - try { - const configPath = CONFIG_FILE_PATTERN[BUILD_TOOL_NAME.VITE]; - - // Check if the configuration file exists - if (await exists(configPath)) { - return true; - } - - // Check if the dependency exists - if (!await hasDependency(DEPENDENCY_NAME.VITE)) { - return false; - } - - return true; - } catch (err) { - console.error(err); - return false; - } -}; - -export const modifyViteConfig = (configFileExtension: string): void => { - if (!isViteProjectSupportFileExtension(configFileExtension)) { - console.error('Unsupported file extension'); - return; - } - - const configFileName = `${VITEJS_CONFIG_BASE_NAME}${configFileExtension}`; - const configPath = path.resolve(process.cwd(), configFileName); - - if (!fs.existsSync(configPath)) { - console.error(`${configFileName} not found`); - return; - } - - const viteConfigCode = fs.readFileSync(configPath, 'utf-8'); - const ast = parse(viteConfigCode, genASTParserOptionsByFileExtension(configFileExtension, 'module')); - - let reactPluginAdded = false; - let onlookBabelPluginAdded = false; - let reactImportAdded = false; - - // Add import for react plugin if it doesn't exist - traverse(ast, { - Program(path) { - const reactImport = path.node.body.find( - node => t.isImportDeclaration(node) && - node.source.value === '@vitejs/plugin-react' - ); - - if (!reactImport) { - path.node.body.unshift( - t.importDeclaration( - [t.importDefaultSpecifier(t.identifier('react'))], - t.stringLiteral('@vitejs/plugin-react') - ) - ); - reactImportAdded = true; - } - } - }); - - traverse(ast, { - CallExpression(path) { - if (t.isIdentifier(path.node.callee, { name: 'defineConfig' })) { - const configArg = path.node.arguments[0]; - if (t.isObjectExpression(configArg)) { - let pluginsProperty = configArg.properties.find( - (prop): prop is t.ObjectProperty => t.isObjectProperty(prop) && t.isIdentifier(prop.key, { name: 'plugins' }) - ); - - if (!pluginsProperty) { - pluginsProperty = t.objectProperty( - t.identifier('plugins'), - t.arrayExpression([]) - ); - configArg.properties.push(pluginsProperty); - } - - const pluginsArray = (pluginsProperty.value as t.ArrayExpression).elements; - - // Find the react plugin - const reactPluginIndex = pluginsArray.findIndex( - (plugin) => - (t.isIdentifier(plugin) && plugin.name === 'react') || - (t.isCallExpression(plugin) && t.isIdentifier(plugin.callee) && plugin.callee.name === 'react') - ); - - if (reactPluginIndex === -1) { - // If react plugin doesn't exist, add it with the Onlook Babel plugin - const reactPlugin = t.callExpression( - t.identifier('react'), - [t.objectExpression([ - t.objectProperty( - t.identifier('babel'), - t.objectExpression([ - t.objectProperty( - t.identifier('plugins'), - t.arrayExpression([t.stringLiteral(ONLOOK_BABEL_PLUGIN)]) - ) - ]) - ) - ])] - ); - pluginsArray.push(reactPlugin); - reactPluginAdded = true; - onlookBabelPluginAdded = true; - } else { - // If react plugin exists, ensure it has the Onlook Babel plugin - const reactPlugin = pluginsArray[reactPluginIndex]; - if (t.isCallExpression(reactPlugin) && reactPlugin.arguments.length === 0) { - // React plugin exists but has no arguments, add the configuration - reactPlugin.arguments.push( - t.objectExpression([ - t.objectProperty( - t.identifier('babel'), - t.objectExpression([ - t.objectProperty( - t.identifier('plugins'), - t.arrayExpression([t.stringLiteral(ONLOOK_BABEL_PLUGIN)]) - ) - ]) - ) - ]) - ); - reactPluginAdded = true; - onlookBabelPluginAdded = true; - } else if (t.isCallExpression(reactPlugin) && reactPlugin.arguments.length > 0) { - // React plugin exists and has arguments, ensure it has the Onlook Babel plugin - const reactConfig = reactPlugin.arguments[0]; - if (t.isObjectExpression(reactConfig)) { - let babelProp = reactConfig.properties.find( - (prop): prop is t.ObjectProperty => t.isObjectProperty(prop) && t.isIdentifier(prop.key, { name: 'babel' }) - ); - - if (!babelProp) { - babelProp = t.objectProperty( - t.identifier('babel'), - t.objectExpression([ - t.objectProperty( - t.identifier('plugins'), - t.arrayExpression([t.stringLiteral(ONLOOK_BABEL_PLUGIN)]) - ) - ]) - ); - reactConfig.properties.push(babelProp); - reactPluginAdded = true; - onlookBabelPluginAdded = true; - } else if (t.isObjectExpression(babelProp.value)) { - let pluginsProp = babelProp.value.properties.find( - (prop): prop is t.ObjectProperty => t.isObjectProperty(prop) && t.isIdentifier(prop.key, { name: 'plugins' }) - ); - - if (!pluginsProp) { - pluginsProp = t.objectProperty( - t.identifier('plugins'), - t.arrayExpression([t.stringLiteral(ONLOOK_BABEL_PLUGIN)]) - ); - babelProp.value.properties.push(pluginsProp); - reactPluginAdded = true; - onlookBabelPluginAdded = true; - } else if (t.isArrayExpression(pluginsProp.value)) { - if (!hasPlugin(pluginsProp.value.elements as t.Expression[], ONLOOK_BABEL_PLUGIN)) { - pluginsProp.value.elements.push(t.stringLiteral(ONLOOK_BABEL_PLUGIN)); - reactPluginAdded = true; - onlookBabelPluginAdded = true; - } - } - } - } - } - } - } - } - }, - }); - - const { code: modifiedCode } = generate(ast, {}, viteConfigCode); - fs.writeFileSync(configPath, modifiedCode, 'utf-8'); - - if (reactPluginAdded) { - console.log(`React plugin added to ${configFileName}`); - } - if (onlookBabelPluginAdded) { - console.log(`${ONLOOK_BABEL_PLUGIN} plugin added to ${configFileName}`); - } - if (reactImportAdded) { - console.log(`React import added to ${configFileName}`); - } - if (!reactPluginAdded && !onlookBabelPluginAdded && !reactImportAdded) { - console.log(`No changes were necessary in ${configFileName}`); - } -}; diff --git a/cli/src/setup/webpack.ts b/cli/src/setup/webpack.ts deleted file mode 100644 index be6af8662..000000000 --- a/cli/src/setup/webpack.ts +++ /dev/null @@ -1,168 +0,0 @@ -import generate from '@babel/generator'; -import { parse } from '@babel/parser'; -import traverse from '@babel/traverse'; -import * as t from '@babel/types'; -import * as fs from 'fs'; -import * as path from 'path'; - -import { - BABELRC_FILE, - BUILD_TOOL_NAME, - CONFIG_FILE_PATTERN, - DEPENDENCY_NAME, - ONLOOK_WEBPACK_PLUGIN, - WEBPACK_CONFIG_BASE_NAME -} from "./constants"; - -import { exists, hasDependency, isSupportFileExtension } from "./utils"; - -export const isWebpackProject = async (): Promise => { - try { - const configPath = CONFIG_FILE_PATTERN[BUILD_TOOL_NAME.WEBPACK]; - - // Check if the configuration file exists - if (!await exists(configPath)) { - return false; - } - - // Check if the dependency exists - if (!await hasDependency(DEPENDENCY_NAME.WEBPACK)) { - return false; - } - - return true; - } catch (err) { - console.error(err); - return false; - } -}; - -/** - * Babel rule to be added to the webpack.config.* file - */ -const babelRule: t.ObjectExpression = t.objectExpression([ - t.objectProperty(t.identifier('test'), t.regExpLiteral('\\.(js|mjs|cjs|ts|tsx|jsx)$')), - t.objectProperty(t.identifier('exclude'), t.regExpLiteral('\\/node_modules\\/')), - t.objectProperty(t.identifier('use'), t.objectExpression([ - t.objectProperty(t.identifier('loader'), t.stringLiteral('babel-loader')), - t.objectProperty(t.identifier('options'), t.objectExpression([ - t.objectProperty(t.identifier('presets'), t.arrayExpression([ - t.stringLiteral('@babel/preset-env'), - t.stringLiteral('@babel/preset-react') - ])) - ])) - ])) -]); - -export function modifyWebpackConfig(configFileExtension: string): void { - if (!isSupportFileExtension(configFileExtension)) { - console.error('Unsupported file extension'); - return; - } - - const configFileName = `${WEBPACK_CONFIG_BASE_NAME}${configFileExtension}`; - - // Define the path to webpack.config.* file - const configPath = path.resolve(process.cwd(), configFileName); - - if (!fs.existsSync(configPath)) { - console.error(`${configFileName} not found`); - return; - } - - fs.readFile(configPath, 'utf8', (err, fileContent) => { - if (err) { - console.error(`Error reading ${configPath}:`, err); - return; - } - - const ast = parse(fileContent, { sourceType: 'module' }); - - let rulesArray: t.Expression[] | undefined; - - // Traverse the AST to find the module.rules array - traverse(ast, { - ObjectProperty(path) { - // @ts-ignore - if (path.node.key.name === 'module' && t.isObjectExpression(path.node.value)) { - const moduleProperties = path.node.value.properties; - moduleProperties.forEach(property => { - if (t.isObjectProperty(property) && - t.isIdentifier(property.key) && - property.key.name === 'rules' && - t.isArrayExpression(property.value)) { - rulesArray = property.value.elements as t.Expression[]; - } - }); - - // If module.rules does not exist, create it - if (!rulesArray) { - const rulesProperty = t.objectProperty( - t.identifier('rules'), - t.arrayExpression([]) - ); - path.node.value.properties.push(rulesProperty); - rulesArray = (rulesProperty.value as t.ArrayExpression).elements as t.Expression[]; - } - } - } - }); - - // Add the babel rule to the rules array - if (rulesArray) { - rulesArray.push(babelRule); - } - - // Generate the updated code - const updatedCode = generate(ast, {}, fileContent).code; - // Write the updated content back to next.config.* file - fs.writeFile(configPath, updatedCode, 'utf8', (err) => { - if (err) { - console.error(`Error writing ${configPath}:`, err); - return; - } - - console.log(`Successfully updated ${configPath}`); - }); - }); -} - -// Path to the .babelrc file -const babelrcPath = path.resolve(BABELRC_FILE); - -// Default .babelrc content if it doesn't exist -const defaultBabelrcContent = { - plugins: [] -}; - -/** - * Modify the .babelrc file to include the "@onlook/react" plugin - */ -export const modifyBabelrc = (): void => { - let babelrcContent: { plugins: string[] }; - - // Check if .babelrc file exists - if (fs.existsSync(babelrcPath)) { - // Read the .babelrc file - const fileContent = fs.readFileSync(babelrcPath, 'utf8'); - babelrcContent = JSON.parse(fileContent); - } else { - // Use default .babelrc content if file does not exist - babelrcContent = defaultBabelrcContent; - } - - // Ensure plugins array exists - if (!Array.isArray(babelrcContent.plugins)) { - babelrcContent.plugins = []; - } - - // Check if "@onlook/react" is already in the plugins array - if (!babelrcContent.plugins.includes(ONLOOK_WEBPACK_PLUGIN)) { - // Add "@onlook/react" to the plugins array - babelrcContent.plugins.push(ONLOOK_WEBPACK_PLUGIN); - } - - // Write the updated content back to the .babelrc file - fs.writeFileSync(babelrcPath, JSON.stringify(babelrcContent, null, 2), 'utf8'); - console.log('.babelrc has been updated with the "@onlook/react" plugin.'); -}; diff --git a/utils/src/create/index.ts b/utils/src/create/index.ts index 696497ae7..99359e7aa 100644 --- a/utils/src/create/index.ts +++ b/utils/src/create/index.ts @@ -3,16 +3,15 @@ import degit from 'degit'; import * as fs from 'fs'; import * as path from 'path'; import { promisify } from 'util'; -import { ProjectCreationStage, type ProgressCallback } from '../models'; +import { CreateStage, type CreateCallback } from '..'; const NEXT_TEMPLATE_REPO = 'onlook-dev/starter'; const execAsync = promisify(exec); - export async function createProject( projectName: string, targetPath: string, - onProgress: ProgressCallback + onProgress: CreateCallback ): Promise { const fullPath = path.join(targetPath, projectName); @@ -23,7 +22,7 @@ export async function createProject( try { // Clone the template using degit - onProgress(ProjectCreationStage.CLONING, `Cloning template...`); + onProgress(CreateStage.CLONING, `Cloning template...`); const emitter = degit(NEXT_TEMPLATE_REPO, { cache: false, force: true, @@ -36,12 +35,12 @@ export async function createProject( process.chdir(fullPath); // Install dependencies - onProgress(ProjectCreationStage.INSTALLING, 'Installing dependencies...'); + onProgress(CreateStage.INSTALLING, 'Installing dependencies...'); await execAsync('npm install'); - onProgress(ProjectCreationStage.COMPLETE, 'Project created successfully!'); + onProgress(CreateStage.COMPLETE, 'Project created successfully!'); } catch (error) { - onProgress(ProjectCreationStage.ERROR, `Project creation failed: ${error}`); + onProgress(CreateStage.ERROR, `Project creation failed: ${error}`); throw error; } } diff --git a/utils/src/index.ts b/utils/src/index.ts index aced60539..5c6c7f0e5 100644 --- a/utils/src/index.ts +++ b/utils/src/index.ts @@ -1,2 +1,26 @@ export { createProject } from './create'; -export { ProjectCreationStage, type ProgressCallback } from './models'; +export { setupProject } from './setup'; + +export enum CreateStage { + CLONING = 'cloning', + INSTALLING = 'installing', + COMPLETE = 'complete', + ERROR = 'error' +} + +export enum VerifyStage { + CHECKING = 'checking', + COMPLETE = 'complete', + ERROR = 'error' +} + +export enum SetupStage { + INSTALLING = 'installing', + CONFIGURING = 'configuring', + COMPLETE = 'complete', + ERROR = 'error' +} + +export type CreateCallback = (stage: CreateStage, message: string) => void; +export type VerifyCallback = (stage: VerifyStage, message: string) => void; +export type SetupCallback = (stage: SetupStage, message: string) => void; diff --git a/utils/src/models.ts b/utils/src/models.ts deleted file mode 100644 index 2feb3a51d..000000000 --- a/utils/src/models.ts +++ /dev/null @@ -1,8 +0,0 @@ -export enum ProjectCreationStage { - CLONING = 'cloning', - INSTALLING = 'installing', - COMPLETE = 'complete', - ERROR = 'error' -} - -export type ProgressCallback = (stage: ProjectCreationStage, message: string) => void; diff --git a/utils/src/setup/index.ts b/utils/src/setup/index.ts index 0aad3739d..ed4be4c34 100755 --- a/utils/src/setup/index.ts +++ b/utils/src/setup/index.ts @@ -1,6 +1,7 @@ +import type { SetupCallback } from '..'; import { Framework } from './frameworks'; -export const setup = async (): Promise => { +export const setupProject = async (targetPath: string, onProgress: SetupCallback): Promise => { try { for (const framework of Framework.getAll()) { const updated = await framework.run(); @@ -8,7 +9,6 @@ export const setup = async (): Promise => { return; } } - console.warn('Cannot determine the project framework.', '\nIf this is unexpected, see: https://github.com/onlook-dev/onlook/wiki/How-to-set-up-my-project%3F#do-it-manually'); } catch (err) { console.error(err); From 9771a29d6b9bdf7d69d664b94ded32263314716c Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sat, 21 Sep 2024 14:24:05 -0400 Subject: [PATCH 23/40] Organize --- utils/src/{setup => frameworks}/constants.ts | 0 utils/src/{setup => frameworks}/cra.ts | 0 utils/src/{setup/frameworks.ts => frameworks/index.ts} | 2 +- utils/src/{setup => frameworks}/next.ts | 0 utils/src/{setup => frameworks}/utils.ts | 0 utils/src/{setup => frameworks}/vite.ts | 0 utils/src/{setup => frameworks}/webpack.ts | 0 utils/src/setup/index.ts | 4 ++-- 8 files changed, 3 insertions(+), 3 deletions(-) rename utils/src/{setup => frameworks}/constants.ts (100%) rename utils/src/{setup => frameworks}/cra.ts (100%) rename utils/src/{setup/frameworks.ts => frameworks/index.ts} (97%) rename utils/src/{setup => frameworks}/next.ts (100%) rename utils/src/{setup => frameworks}/utils.ts (100%) rename utils/src/{setup => frameworks}/vite.ts (100%) rename utils/src/{setup => frameworks}/webpack.ts (100%) diff --git a/utils/src/setup/constants.ts b/utils/src/frameworks/constants.ts similarity index 100% rename from utils/src/setup/constants.ts rename to utils/src/frameworks/constants.ts diff --git a/utils/src/setup/cra.ts b/utils/src/frameworks/cra.ts similarity index 100% rename from utils/src/setup/cra.ts rename to utils/src/frameworks/cra.ts diff --git a/utils/src/setup/frameworks.ts b/utils/src/frameworks/index.ts similarity index 97% rename from utils/src/setup/frameworks.ts rename to utils/src/frameworks/index.ts index c5fb70fc8..a65c0269d 100644 --- a/utils/src/setup/frameworks.ts +++ b/utils/src/frameworks/index.ts @@ -20,7 +20,7 @@ export class Framework { public readonly buildToolName: BUILD_TOOL_NAME ) { } - run = async (): Promise => { + setup = async (): Promise => { if (await this.identify()) { console.log(`This is a ${this.name} project.`); diff --git a/utils/src/setup/next.ts b/utils/src/frameworks/next.ts similarity index 100% rename from utils/src/setup/next.ts rename to utils/src/frameworks/next.ts diff --git a/utils/src/setup/utils.ts b/utils/src/frameworks/utils.ts similarity index 100% rename from utils/src/setup/utils.ts rename to utils/src/frameworks/utils.ts diff --git a/utils/src/setup/vite.ts b/utils/src/frameworks/vite.ts similarity index 100% rename from utils/src/setup/vite.ts rename to utils/src/frameworks/vite.ts diff --git a/utils/src/setup/webpack.ts b/utils/src/frameworks/webpack.ts similarity index 100% rename from utils/src/setup/webpack.ts rename to utils/src/frameworks/webpack.ts diff --git a/utils/src/setup/index.ts b/utils/src/setup/index.ts index ed4be4c34..4ddeadcfb 100755 --- a/utils/src/setup/index.ts +++ b/utils/src/setup/index.ts @@ -1,10 +1,10 @@ import type { SetupCallback } from '..'; -import { Framework } from './frameworks'; +import { Framework } from '../frameworks'; export const setupProject = async (targetPath: string, onProgress: SetupCallback): Promise => { try { for (const framework of Framework.getAll()) { - const updated = await framework.run(); + const updated = await framework.setup(); if (updated) { return; } From a7c0ba8b502439d581de0e441a0b503f9025838a Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sat, 21 Sep 2024 15:50:22 -0400 Subject: [PATCH 24/40] Working verify project --- app/common/constants.ts | 2 + app/electron/main/events/create.ts | 28 +- .../ProjectsTab/Create/Load/Verify.tsx | 40 ++- .../projects/ProjectsTab/Create/New/Setup.tsx | 4 +- .../projects/ProjectsTab/Create/index.tsx | 4 +- utils/src/constants.ts | 72 +++++ utils/src/frameworks/constants.ts | 66 ---- utils/src/frameworks/cra.ts | 12 +- utils/src/frameworks/frameworks.ts | 46 +++ utils/src/frameworks/index.ts | 4 +- utils/src/frameworks/next.ts | 294 +++++++++--------- utils/src/frameworks/utils.ts | 133 -------- utils/src/frameworks/vite.ts | 24 +- utils/src/frameworks/webpack.ts | 14 +- utils/src/index.ts | 1 + utils/src/utils.ts | 134 ++++++++ utils/src/verify/index.ts | 20 ++ 17 files changed, 506 insertions(+), 392 deletions(-) create mode 100644 utils/src/constants.ts delete mode 100644 utils/src/frameworks/constants.ts create mode 100644 utils/src/frameworks/frameworks.ts delete mode 100644 utils/src/frameworks/utils.ts create mode 100644 utils/src/utils.ts diff --git a/app/common/constants.ts b/app/common/constants.ts index 3c076cc31..b26582887 100644 --- a/app/common/constants.ts +++ b/app/common/constants.ts @@ -86,6 +86,8 @@ export enum MainChannels { // Create CREATE_NEW_PROJECT = 'create-new-project', CREATE_NEW_PROJECT_CALLBACK = 'create-new-project-callback', + VERIFY_PROJECT = 'verify-project', + VERIFY_PROJECT_CALLBACK = 'verify-project-callback', } export enum Links { diff --git a/app/electron/main/events/create.ts b/app/electron/main/events/create.ts index 34df27e23..71eff34a7 100644 --- a/app/electron/main/events/create.ts +++ b/app/electron/main/events/create.ts @@ -1,14 +1,18 @@ -import { ProgressCallback, ProjectCreationStage, createProject } from '@onlook/utils'; +import { + CreateCallback, + CreateStage, + VerifyCallback, + VerifyStage, + createProject, + verifyProject, +} from '@onlook/utils'; import { ipcMain } from 'electron'; import { mainWindow } from '..'; import { MainChannels } from '/common/constants'; export function listenForCreateMessages() { ipcMain.handle(MainChannels.CREATE_NEW_PROJECT, (e: Electron.IpcMainInvokeEvent, args) => { - const progressCallback: ProgressCallback = ( - stage: ProjectCreationStage, - message: string, - ) => { + const progressCallback: CreateCallback = (stage: CreateStage, message: string) => { mainWindow?.webContents.send(MainChannels.CREATE_NEW_PROJECT_CALLBACK, { stage, message, @@ -19,8 +23,14 @@ export function listenForCreateMessages() { return createProject(name, path, progressCallback); }); - ipcMain.handle( - MainChannels.VERIFY_PROJECT, - (e: Electron.IpcMainInvokeEvent, args: string) => {}, - ); + ipcMain.handle(MainChannels.VERIFY_PROJECT, (e: Electron.IpcMainInvokeEvent, args: string) => { + const progressCallback: VerifyCallback = (stage: VerifyStage, message: string) => { + mainWindow?.webContents.send(MainChannels.VERIFY_PROJECT_CALLBACK, { + stage, + message, + }); + }; + const path = args as string; + return verifyProject(path, progressCallback); + }); } diff --git a/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx b/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx index 1469626c6..685a3e08d 100644 --- a/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx +++ b/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx @@ -7,10 +7,12 @@ import { CardHeader, CardTitle, } from '@/components/ui/card'; +import { VerifyStage } from '@onlook/utils'; import { CheckCircledIcon, ExclamationTriangleIcon, ShadowIcon } from '@radix-ui/react-icons'; import clsx from 'clsx'; import { useEffect, useState } from 'react'; import { StepProps } from '..'; +import { MainChannels } from '/common/constants'; export const LoadVerifyProject = ({ props: { projectData, setProjectData, currentStep, totalSteps, prevStep, nextStep }, @@ -19,18 +21,44 @@ export const LoadVerifyProject = ({ }) => { const [isVerifying, setIsVerifying] = useState(true); const [isInstalled, setIsInstalled] = useState(null); + const [verifyMessage, setVerifyMessage] = useState('Rummaging...'); useEffect(() => { - if (isVerifying) { - setTimeout(() => { - setIsInstalled(true); - }, 1000); + if (!projectData.folderPath) { + throw new Error('Folder path is not provided'); } + window.api + .invoke(MainChannels.VERIFY_PROJECT, projectData.folderPath) + .then((isInstalled) => { + setIsInstalled(isInstalled as boolean); + }); + + window.api.on( + MainChannels.VERIFY_PROJECT_CALLBACK, + ({ stage, message }: { stage: VerifyStage; message: string }) => { + setVerifyMessage(message); + if (stage === 'checking') { + setIsVerifying(true); + setIsInstalled(false); + } else if (stage === 'complete') { + setIsVerifying(false); + } + }, + ); + return () => { + window.api.removeAllListeners(MainChannels.VERIFY_PROJECT_CALLBACK); + }; }, []); + async function installOnlook() { setIsInstalled(true); } + function handleSelectDifferentFolder() { + setProjectData({ folderPath: undefined }); + prevStep(); + } + return ( @@ -53,7 +81,7 @@ export const LoadVerifyProject = ({ {isVerifying ? (
-

Rummaging...

+

{verifyMessage}

) : (

{`${currentStep + 1} of ${totalSteps}`}

- diff --git a/app/src/routes/projects/ProjectsTab/Create/New/Setup.tsx b/app/src/routes/projects/ProjectsTab/Create/New/Setup.tsx index c778d4ab4..a598ac9e5 100644 --- a/app/src/routes/projects/ProjectsTab/Create/New/Setup.tsx +++ b/app/src/routes/projects/ProjectsTab/Create/New/Setup.tsx @@ -8,7 +8,7 @@ import { CardTitle, } from '@/components/ui/card'; import { Progress } from '@/components/ui/progress'; -import type { ProjectCreationStage } from '@onlook/utils'; +import type { CreateStage } from '@onlook/utils'; import { CheckCircledIcon } from '@radix-ui/react-icons'; import { useEffect, useState } from 'react'; import { StepProps } from '..'; @@ -26,7 +26,7 @@ export const NewSetupProject = ({ useEffect(() => { window.api.on( MainChannels.CREATE_NEW_PROJECT_CALLBACK, - ({ stage, message }: { stage: ProjectCreationStage; message: string }) => { + ({ stage, message }: { stage: CreateStage; message: string }) => { setMessage(message); if (stage === 'cloning') { setProgress(30); diff --git a/app/src/routes/projects/ProjectsTab/Create/index.tsx b/app/src/routes/projects/ProjectsTab/Create/index.tsx index 1c1395435..bbee1f61e 100644 --- a/app/src/routes/projects/ProjectsTab/Create/index.tsx +++ b/app/src/routes/projects/ProjectsTab/Create/index.tsx @@ -85,10 +85,10 @@ const CreateProject = ({ return ; } if (currentStep === 1) { - return ; + return ; } if (currentStep === 2) { - return ; + return ; } if (currentStep === 3) { return ; diff --git a/utils/src/constants.ts b/utils/src/constants.ts new file mode 100644 index 000000000..694ba0a4f --- /dev/null +++ b/utils/src/constants.ts @@ -0,0 +1,72 @@ +export enum BUILD_TOOL_NAME { + NEXT = "next", + WEBPACK = "webpack", + CRA = "cra", + VITE = "vite", +} + +export enum DEPENDENCY_NAME { + NEXT = "next", + WEBPACK = "webpack", + CRA = "react-scripts", + VITE = "vite" +} + +export enum CONFIG_BASE_NAME { + NEXTJS = 'next.config', + WEBPACK = 'webpack.config', + VITEJS = 'vite.config' +} + +export const CONFIG_FILE_PATTERN: Record = { + [BUILD_TOOL_NAME.NEXT]: `${CONFIG_BASE_NAME.NEXTJS}.*`, + [BUILD_TOOL_NAME.WEBPACK]: `${CONFIG_BASE_NAME.WEBPACK}.*`, + [BUILD_TOOL_NAME.VITE]: `${CONFIG_BASE_NAME.VITEJS}.*`, + [BUILD_TOOL_NAME.CRA]: '' +} + +export const PACKAGE_JSON = 'package.json'; + +export enum LOCK_FILE_NAME { + YARN = 'yarn.lock', + BUN = 'bun.lockb', + PNPM = 'pnpm-lock.yaml' +} + +export enum PACKAGE_MANAGER { + YARN = 'yarn', + NPM = 'npm', + PNPM = 'pnpm', + BUN = 'bun' +} + +export enum ONLOOK_PLUGIN { + NEXTJS = '@onlook/nextjs', + WEBPACK = '@onlook/react', + BABEL = '@onlook/babel-plugin-react' +} + +export const NEXTJS_COMMON_FILES = ['pages', 'app', 'src/pages', 'src/app']; +export const CRA_COMMON_FILES = ['public', 'src']; + +export const CONFIG_OVERRIDES_FILE = 'config-overrides.js'; +export const BABELRC_FILE = '.babelrc'; + +export enum FILE_EXTENSION { + JS = '.js', + MJS = '.mjs', + TS = '.ts', +} + +export const NEXT_DEPENDENCIES = [ONLOOK_PLUGIN.NEXTJS]; +export const CRA_DEPENDENCIES = [ONLOOK_PLUGIN.BABEL, 'customize-cra', 'react-app-rewired']; +export const VITE_DEPENDENCIES = [ONLOOK_PLUGIN.BABEL]; + +export const WEBPACK_DEPENDENCIES = [ + ONLOOK_PLUGIN.BABEL, + 'babel-loader', + '@babel/preset-react', + '@babel/core', + '@babel/preset-env', + 'webpack' +]; diff --git a/utils/src/frameworks/constants.ts b/utils/src/frameworks/constants.ts deleted file mode 100644 index 4d889a2d7..000000000 --- a/utils/src/frameworks/constants.ts +++ /dev/null @@ -1,66 +0,0 @@ -export enum BUILD_TOOL_NAME { - NEXT = "next", - WEBPACK = "webpack", - CRA = "cra", - VITE = "vite", -} - -export enum DEPENDENCY_NAME { - NEXT = "next", - WEBPACK = "webpack", - CRA = "react-scripts", - VITE = "vite" -} - -export const NEXTJS_CONFIG_BASE_NAME = 'next.config'; -export const WEBPACK_CONFIG_BASE_NAME = 'webpack.config'; -export const VITEJS_CONFIG_BASE_NAME = 'vite.config'; - -export const CONFIG_FILE_PATTERN: Record = { - [BUILD_TOOL_NAME.NEXT]: `${NEXTJS_CONFIG_BASE_NAME}.*`, - [BUILD_TOOL_NAME.WEBPACK]: `${WEBPACK_CONFIG_BASE_NAME}.*`, - [BUILD_TOOL_NAME.VITE]: `${VITEJS_CONFIG_BASE_NAME}.*`, - [BUILD_TOOL_NAME.CRA]: '' -} - -export const PACKAGE_JSON = 'package.json'; - -export enum LOCK_FILE_NAME { - YARN = 'yarn.lock', - BUN = 'bun.lockb', - PNPM = 'pnpm-lock.yaml' -} - -export enum PACKAGE_MANAGER { - YARN = 'yarn', - NPM = 'npm', - PNPM = 'pnpm', - BUN = 'bun' -} - -export const ONLOOK_NEXTJS_PLUGIN = '@onlook/nextjs'; -export const ONLOOK_WEBPACK_PLUGIN = '@onlook/react'; -export const ONLOOK_BABEL_PLUGIN = '@onlook/babel-plugin-react'; - -export const NEXTJS_COMMON_FILES = ['pages', 'app', 'src/pages', 'src/app']; -export const CRA_COMMON_FILES = ['public', 'src']; - -export const CONFIG_OVERRIDES_FILE = 'config-overrides.js'; -export const BABELRC_FILE = '.babelrc'; - -export const JS_FILE_EXTENSION = '.js'; -export const MJS_FILE_EXTENSION = '.mjs'; -export const TS_FILE_EXTENSION = '.ts'; - -export const NEXT_DEPENDENCIES = [ONLOOK_NEXTJS_PLUGIN]; -export const CRA_DEPENDENCIES = [ONLOOK_BABEL_PLUGIN, 'customize-cra', 'react-app-rewired']; -export const VITE_DEPENDENCIES = [ONLOOK_BABEL_PLUGIN]; - -export const WEBPACK_DEPENDENCIES = [ - ONLOOK_BABEL_PLUGIN, - 'babel-loader', - '@babel/preset-react', - '@babel/core', - '@babel/preset-env', - 'webpack' -]; diff --git a/utils/src/frameworks/cra.ts b/utils/src/frameworks/cra.ts index a734888d4..2fb76b964 100644 --- a/utils/src/frameworks/cra.ts +++ b/utils/src/frameworks/cra.ts @@ -1,5 +1,5 @@ -import { CONFIG_OVERRIDES_FILE, CRA_COMMON_FILES, DEPENDENCY_NAME, JS_FILE_EXTENSION, ONLOOK_WEBPACK_PLUGIN, PACKAGE_JSON } from "./constants"; -import { exists, genASTParserOptionsByFileExtension, hasDependency } from "./utils"; +import { CONFIG_OVERRIDES_FILE, CRA_COMMON_FILES, DEPENDENCY_NAME, FILE_EXTENSION, ONLOOK_PLUGIN, PACKAGE_JSON } from "../constants"; +import { exists, genASTParserOptionsByFileExtension, hasDependency } from "../utils"; import generate from '@babel/generator'; import { parse } from '@babel/parser'; @@ -19,7 +19,7 @@ const defaultContent = ` module.exports = override( ...addBabelPlugins( - '${ONLOOK_WEBPACK_PLUGIN}' + '${ONLOOK_PLUGIN.WEBPACK}' ) ); `; @@ -44,7 +44,7 @@ export const ensureConfigOverrides = (): void => { return; } // Read the existing file - const ast = parse(fileContent, genASTParserOptionsByFileExtension(JS_FILE_EXTENSION)); + const ast = parse(fileContent, genASTParserOptionsByFileExtension(FILE_EXTENSION.JS)); let hasCustomizeCraImport = false; let hasOnlookReactPlugin = false; @@ -68,7 +68,7 @@ export const ensureConfigOverrides = (): void => { path.node.arguments.forEach(arg => { if (t.isSpreadElement(arg) && t.isCallExpression(arg.argument) && t.isIdentifier(arg.argument.callee, { name: 'addBabelPlugins' }) && - arg.argument.arguments.some(pluginArg => t.isStringLiteral(pluginArg, { value: ONLOOK_WEBPACK_PLUGIN }))) { + arg.argument.arguments.some(pluginArg => t.isStringLiteral(pluginArg, { value: ONLOOK_PLUGIN.WEBPACK }))) { hasOnlookReactPlugin = true; } }); @@ -98,7 +98,7 @@ export const ensureConfigOverrides = (): void => { // @ts-ignore path.node.right.arguments.push( t.spreadElement(t.callExpression(t.identifier('addBabelPlugins'), [ - t.stringLiteral(ONLOOK_WEBPACK_PLUGIN) + t.stringLiteral(ONLOOK_PLUGIN.WEBPACK) ])) ); } diff --git a/utils/src/frameworks/frameworks.ts b/utils/src/frameworks/frameworks.ts new file mode 100644 index 000000000..a3aed5668 --- /dev/null +++ b/utils/src/frameworks/frameworks.ts @@ -0,0 +1,46 @@ +import { BUILD_TOOL_NAME, CONFIG_FILE_PATTERN, CRA_DEPENDENCIES, NEXT_DEPENDENCIES, VITE_DEPENDENCIES, WEBPACK_DEPENDENCIES } from "../constants"; +import { getFileExtensionByPattern, installPackages } from "../utils"; +import { isCRAProject, modifyCRAConfig } from "./cra"; +import { isNextJsProject, modifyNextConfig } from "./next"; +import { isViteJsProject, modifyViteConfig } from "./vite"; +import { isWebpackProject, modifyWebpackConfig } from "./webpack"; + + +export class Framework { + static readonly NEXT = new Framework("Next.js", isNextJsProject, modifyNextConfig, NEXT_DEPENDENCIES, BUILD_TOOL_NAME.NEXT); + static readonly VITE = new Framework("Vite", isViteJsProject, modifyViteConfig, VITE_DEPENDENCIES, BUILD_TOOL_NAME.VITE); + static readonly WEBPACK = new Framework("Webpack", isWebpackProject, modifyWebpackConfig, WEBPACK_DEPENDENCIES, BUILD_TOOL_NAME.WEBPACK); + static readonly CRA = new Framework("Create React App", isCRAProject, modifyCRAConfig, CRA_DEPENDENCIES, BUILD_TOOL_NAME.CRA); + + private constructor( + public readonly name: string, + public readonly identify: () => Promise, + public readonly updateConfig: (configFileExtension: string) => void, + public readonly dependencies: string[], + public readonly buildToolName: BUILD_TOOL_NAME + ) { } + + setup = async (): Promise => { + if (await this.identify()) { + console.log(`This is a ${this.name} project.`); + + await installPackages(this.dependencies); + + const configFileExtension = await getFileExtensionByPattern(process.cwd(), CONFIG_FILE_PATTERN[this.buildToolName]); + if (configFileExtension) { + await this.updateConfig(configFileExtension); + } + return true; + } + return false; + } + + static getAll(): Framework[] { + return [ + this.NEXT, + this.VITE, + this.WEBPACK, + this.CRA, + ]; + } +} diff --git a/utils/src/frameworks/index.ts b/utils/src/frameworks/index.ts index a65c0269d..a3aed5668 100644 --- a/utils/src/frameworks/index.ts +++ b/utils/src/frameworks/index.ts @@ -1,7 +1,7 @@ -import { BUILD_TOOL_NAME, CONFIG_FILE_PATTERN, CRA_DEPENDENCIES, NEXT_DEPENDENCIES, VITE_DEPENDENCIES, WEBPACK_DEPENDENCIES } from "./constants"; +import { BUILD_TOOL_NAME, CONFIG_FILE_PATTERN, CRA_DEPENDENCIES, NEXT_DEPENDENCIES, VITE_DEPENDENCIES, WEBPACK_DEPENDENCIES } from "../constants"; +import { getFileExtensionByPattern, installPackages } from "../utils"; import { isCRAProject, modifyCRAConfig } from "./cra"; import { isNextJsProject, modifyNextConfig } from "./next"; -import { getFileExtensionByPattern, installPackages } from "./utils"; import { isViteJsProject, modifyViteConfig } from "./vite"; import { isWebpackProject, modifyWebpackConfig } from "./webpack"; diff --git a/utils/src/frameworks/next.ts b/utils/src/frameworks/next.ts index 922212219..d3ba6659d 100644 --- a/utils/src/frameworks/next.ts +++ b/utils/src/frameworks/next.ts @@ -8,177 +8,177 @@ import traverse from '@babel/traverse'; import * as t from '@babel/types'; import { - checkVariableDeclarationExist, - exists, - genASTParserOptionsByFileExtension, - genImportDeclaration, - hasDependency, - isSupportFileExtension -} from './utils'; + checkVariableDeclarationExist, + exists, + genASTParserOptionsByFileExtension, + genImportDeclaration, + hasDependency, + isSupportFileExtension +} from '../utils'; import { - BUILD_TOOL_NAME, - CONFIG_FILE_PATTERN, - DEPENDENCY_NAME, - NEXTJS_COMMON_FILES, - NEXTJS_CONFIG_BASE_NAME, - ONLOOK_NEXTJS_PLUGIN, -} from './constants'; + BUILD_TOOL_NAME, + CONFIG_BASE_NAME, + CONFIG_FILE_PATTERN, + DEPENDENCY_NAME, + NEXTJS_COMMON_FILES, + ONLOOK_PLUGIN, +} from '../constants'; export const isNextJsProject = async (): Promise => { - try { - const configPath = CONFIG_FILE_PATTERN[BUILD_TOOL_NAME.NEXT]; + try { + const configPath = CONFIG_FILE_PATTERN[BUILD_TOOL_NAME.NEXT]; - // Check if the configuration file exists - if (await exists(configPath)) { - return true; - } + // Check if the configuration file exists + if (await exists(configPath)) { + return true; + } - // Check if the dependency exists - if (!await hasDependency(DEPENDENCY_NAME.NEXT)) { - return false; - } + // Check if the dependency exists + if (!await hasDependency(DEPENDENCY_NAME.NEXT)) { + return false; + } - // Check if one of the directories exists - const directoryExists = await Promise.all(NEXTJS_COMMON_FILES.map(exists)); + // Check if one of the directories exists + const directoryExists = await Promise.all(NEXTJS_COMMON_FILES.map(exists)); - return directoryExists.some(Boolean); - } catch (err) { - console.error(err); - return false; - } + return directoryExists.some(Boolean); + } catch (err) { + console.error(err); + return false; + } }; export const modifyNextConfig = (configFileExtension: string): void => { - if (!isSupportFileExtension(configFileExtension)) { - console.error('Unsupported file extension'); - return; - } - - const configFileName = `${NEXTJS_CONFIG_BASE_NAME}${configFileExtension}`; - - // Define the path to next.config.* file - const configPath = path.resolve(process.cwd(), configFileName); - - if (!fs.existsSync(configPath)) { - console.error(`${configFileName} not found`); - return; - } - - console.log(`Adding ${ONLOOK_NEXTJS_PLUGIN} plugin into ${configFileName} file...`); - - // Read the existing next.config.* file - fs.readFile(configPath, 'utf8', (err, data) => { - if (err) { - console.error(`Error reading ${configPath}:`, err); - return; + if (!isSupportFileExtension(configFileExtension)) { + console.error('Unsupported file extension'); + return; } - const astParserOption = genASTParserOptionsByFileExtension(configFileExtension); + const configFileName = `${CONFIG_BASE_NAME.NEXTJS}${configFileExtension}`; - // Parse the file content to an AST - const ast = parse(data, astParserOption); + // Define the path to next.config.* file + const configPath = path.resolve(process.cwd(), configFileName); - let hasPathImport = false; - - // Traverse the AST to find the experimental.swcPlugins array - traverse(ast, { - VariableDeclarator(path) { - // check if path is imported in .js file - if (checkVariableDeclarationExist(path, 'path')) { - hasPathImport = true; - } - }, - ImportDeclaration(path) { - // check if path is imported in .mjs file - if (path.node.source.value === 'path') { - hasPathImport = true; - } - }, - ObjectExpression(path) { - const properties = path.node.properties; - let experimentalProperty: t.ObjectProperty | undefined; - - // Find the experimental property - properties.forEach(prop => { - if (t.isObjectProperty(prop) && t.isIdentifier(prop.key, { name: 'experimental' })) { - experimentalProperty = prop; - } - }); + if (!fs.existsSync(configPath)) { + console.error(`${configFileName} not found`); + return; + } - if (!experimentalProperty) { - // If experimental property is not found, create it - experimentalProperty = t.objectProperty( - t.identifier('experimental'), - t.objectExpression([]) - ); - properties.push(experimentalProperty); - } + console.log(`Adding ${ONLOOK_PLUGIN.NEXTJS} plugin into ${configFileName} file...`); - // Ensure experimental is an ObjectExpression - if (!t.isObjectExpression(experimentalProperty.value)) { - experimentalProperty.value = t.objectExpression([]); + // Read the existing next.config.* file + fs.readFile(configPath, 'utf8', (err, data) => { + if (err) { + console.error(`Error reading ${configPath}:`, err); + return; } - const experimentalProperties = experimentalProperty.value.properties; - let swcPluginsProperty: t.ObjectProperty | undefined; - - // Find the swcPlugins property - experimentalProperties.forEach(prop => { - if (t.isObjectProperty(prop) && t.isIdentifier(prop.key, { name: 'swcPlugins' })) { - swcPluginsProperty = prop; - } + const astParserOption = genASTParserOptionsByFileExtension(configFileExtension); + + // Parse the file content to an AST + const ast = parse(data, astParserOption); + + let hasPathImport = false; + + // Traverse the AST to find the experimental.swcPlugins array + traverse(ast, { + VariableDeclarator(path) { + // check if path is imported in .js file + if (checkVariableDeclarationExist(path, 'path')) { + hasPathImport = true; + } + }, + ImportDeclaration(path) { + // check if path is imported in .mjs file + if (path.node.source.value === 'path') { + hasPathImport = true; + } + }, + ObjectExpression(path) { + const properties = path.node.properties; + let experimentalProperty: t.ObjectProperty | undefined; + + // Find the experimental property + properties.forEach(prop => { + if (t.isObjectProperty(prop) && t.isIdentifier(prop.key, { name: 'experimental' })) { + experimentalProperty = prop; + } + }); + + if (!experimentalProperty) { + // If experimental property is not found, create it + experimentalProperty = t.objectProperty( + t.identifier('experimental'), + t.objectExpression([]) + ); + properties.push(experimentalProperty); + } + + // Ensure experimental is an ObjectExpression + if (!t.isObjectExpression(experimentalProperty.value)) { + experimentalProperty.value = t.objectExpression([]); + } + + const experimentalProperties = experimentalProperty.value.properties; + let swcPluginsProperty: t.ObjectProperty | undefined; + + // Find the swcPlugins property + experimentalProperties.forEach(prop => { + if (t.isObjectProperty(prop) && t.isIdentifier(prop.key, { name: 'swcPlugins' })) { + swcPluginsProperty = prop; + } + }); + + if (!swcPluginsProperty) { + // If swcPlugins property is not found, create it + swcPluginsProperty = t.objectProperty( + t.identifier('swcPlugins'), + t.arrayExpression([]) + ); + experimentalProperties.push(swcPluginsProperty); + } + + // Ensure swcPlugins is an ArrayExpression + if (!t.isArrayExpression(swcPluginsProperty.value)) { + swcPluginsProperty.value = t.arrayExpression([]); + } + + // Add the new plugin configuration to swcPlugins array + const pluginConfig = t.arrayExpression([ + t.stringLiteral(ONLOOK_PLUGIN.NEXTJS), + t.objectExpression([ + t.objectProperty( + t.identifier('root'), + t.callExpression(t.memberExpression(t.identifier('path'), t.identifier('resolve')), [t.stringLiteral('.')]) + ) + ]) + ]); + + swcPluginsProperty.value.elements.push(pluginConfig); + + // Stop traversing after the modification + path.stop(); + } }); - if (!swcPluginsProperty) { - // If swcPlugins property is not found, create it - swcPluginsProperty = t.objectProperty( - t.identifier('swcPlugins'), - t.arrayExpression([]) - ); - experimentalProperties.push(swcPluginsProperty); + // If 'path' is not imported, add the import statement + if (!hasPathImport) { + const importDeclaration = genImportDeclaration(configFileExtension, 'path'); + importDeclaration && ast.program.body.unshift(importDeclaration); } - // Ensure swcPlugins is an ArrayExpression - if (!t.isArrayExpression(swcPluginsProperty.value)) { - swcPluginsProperty.value = t.arrayExpression([]); - } + // Generate the modified code from the AST + const updatedCode = generate(ast, {}, data).code; - // Add the new plugin configuration to swcPlugins array - const pluginConfig = t.arrayExpression([ - t.stringLiteral(ONLOOK_NEXTJS_PLUGIN), - t.objectExpression([ - t.objectProperty( - t.identifier('root'), - t.callExpression(t.memberExpression(t.identifier('path'), t.identifier('resolve')), [t.stringLiteral('.')]) - ) - ]) - ]); - - swcPluginsProperty.value.elements.push(pluginConfig); - - // Stop traversing after the modification - path.stop(); - } - }); + // Write the updated content back to next.config.* file + fs.writeFile(configPath, updatedCode, 'utf8', (err) => { + if (err) { + console.error(`Error writing ${configPath}:`, err); + return; + } - // If 'path' is not imported, add the import statement - if (!hasPathImport) { - const importDeclaration = genImportDeclaration(configFileExtension, 'path'); - importDeclaration && ast.program.body.unshift(importDeclaration); - } - - // Generate the modified code from the AST - const updatedCode = generate(ast, {}, data).code; - - // Write the updated content back to next.config.* file - fs.writeFile(configPath, updatedCode, 'utf8', (err) => { - if (err) { - console.error(`Error writing ${configPath}:`, err); - return; - } - - console.log(`Successfully updated ${configPath}`); + console.log(`Successfully updated ${configPath}`); + }); }); - }); }; diff --git a/utils/src/frameworks/utils.ts b/utils/src/frameworks/utils.ts deleted file mode 100644 index 7799af6df..000000000 --- a/utils/src/frameworks/utils.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { NodePath } from '@babel/traverse'; -import * as t from '@babel/types'; -import { execSync } from 'child_process'; -import * as glob from 'glob'; -import * as path from 'path'; -import { - JS_FILE_EXTENSION, - LOCK_FILE_NAME, - MJS_FILE_EXTENSION, - PACKAGE_JSON, - PACKAGE_MANAGER, - TS_FILE_EXTENSION -} from './constants'; - -export const exists = async (filePattern: string): Promise => { - try { - const pattern = path.resolve(process.cwd(), filePattern); - const files = getFileNamesByPattern(pattern); - return files.length > 0; - } catch (err) { - console.error(err); - return false; - } -}; - -export const getFileNamesByPattern = (pattern: string): string[] => glob.globSync(pattern); - -export const installPackages = async (packages: string[]): Promise => { - const packageManager = await getPackageManager(); - const command = packageManager === PACKAGE_MANAGER.YARN ? 'yarn add -D' : `${packageManager} install -D`; - - console.log("Package manager found:", packageManager) - console.log("\n$", `${command} ${packages.join(' ')}`) - - execSync(`${command} ${packages.join(' ')}`, { stdio: 'inherit' }); -}; - -export const getPackageManager = async (): Promise => { - try { - if (await exists(LOCK_FILE_NAME.YARN)) { - return PACKAGE_MANAGER.YARN; - } - if (await exists(LOCK_FILE_NAME.PNPM)) { - return PACKAGE_MANAGER.PNPM; - } - if (await exists(LOCK_FILE_NAME.BUN)) { - return PACKAGE_MANAGER.BUN; - } - return PACKAGE_MANAGER.NPM; - } catch (e) { - console.error("Error determining package manager, using npm by default", e) - return PACKAGE_MANAGER.NPM - } - -}; - -export const hasDependency = async (dependencyName: string): Promise => { - const packageJsonPath = path.resolve(PACKAGE_JSON); - if (await exists(packageJsonPath)) { - const packageJson = require(packageJsonPath); - return ( - (packageJson.dependencies && packageJson.dependencies[dependencyName]) || - (packageJson.devDependencies && packageJson.devDependencies[dependencyName]) - ); - } - return false; -}; - -export const getFileExtensionByPattern = async (dir: string, filePattern: string): Promise => { - const fullDirPattern = path.resolve(dir, filePattern); - const files = await getFileNamesByPattern(fullDirPattern); - - if (files.length > 0) { - return path.extname(files[0]); - } - - return null; -}; - -export const genASTParserOptionsByFileExtension = (fileExtension: string, sourceType: string = 'module'): object => { - switch (fileExtension) { - case JS_FILE_EXTENSION: - return { - sourceType: sourceType - }; - case MJS_FILE_EXTENSION: - return { - sourceType: sourceType, - plugins: ['jsx'] - }; - case TS_FILE_EXTENSION: - return { - sourceType: sourceType, - plugins: ['typescript'] - }; - default: - return {}; - } -}; - -export const genImportDeclaration = (fileExtension: string, dependency: string): t.VariableDeclaration | t.ImportDeclaration | null => { - switch (fileExtension) { - case JS_FILE_EXTENSION: - return t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier(dependency), - t.callExpression(t.identifier('require'), [t.stringLiteral(dependency)]) - ) - ]); - case MJS_FILE_EXTENSION: - return t.importDeclaration( - [t.importDefaultSpecifier(t.identifier(dependency))], - t.stringLiteral(dependency) - ); - default: - return null; - } -}; - -export const checkVariableDeclarationExist = (path: NodePath, dependency: string): boolean => { - return t.isIdentifier(path.node.id, { name: dependency }) && - t.isCallExpression(path.node.init) && - (path.node.init.callee as t.V8IntrinsicIdentifier).name === 'require' && - (path.node.init.arguments[0] as any).value === dependency; -}; - -export const isSupportFileExtension = (fileExtension: string): boolean => { - return [JS_FILE_EXTENSION, MJS_FILE_EXTENSION].indexOf(fileExtension) !== -1; -}; - -export const isViteProjectSupportFileExtension = (fileExtension: string): boolean => { - return [JS_FILE_EXTENSION, TS_FILE_EXTENSION].indexOf(fileExtension) !== -1; -}; diff --git a/utils/src/frameworks/vite.ts b/utils/src/frameworks/vite.ts index b6ca41d1b..f102ca8b8 100644 --- a/utils/src/frameworks/vite.ts +++ b/utils/src/frameworks/vite.ts @@ -7,17 +7,17 @@ import * as path from 'path'; import { BUILD_TOOL_NAME, + CONFIG_BASE_NAME, CONFIG_FILE_PATTERN, DEPENDENCY_NAME, - ONLOOK_BABEL_PLUGIN, - VITEJS_CONFIG_BASE_NAME -} from "./constants"; + ONLOOK_PLUGIN, +} from "../constants"; import { exists, genASTParserOptionsByFileExtension, hasDependency, isViteProjectSupportFileExtension -} from "./utils"; +} from "../utils"; // Function to check if a plugin is already in the array function hasPlugin(pluginsArray: t.Expression[], pluginName: string): boolean { @@ -56,7 +56,7 @@ export const modifyViteConfig = (configFileExtension: string): void => { return; } - const configFileName = `${VITEJS_CONFIG_BASE_NAME}${configFileExtension}`; + const configFileName = `${CONFIG_BASE_NAME.VITEJS}${configFileExtension}`; const configPath = path.resolve(process.cwd(), configFileName); if (!fs.existsSync(configPath)) { @@ -127,7 +127,7 @@ export const modifyViteConfig = (configFileExtension: string): void => { t.objectExpression([ t.objectProperty( t.identifier('plugins'), - t.arrayExpression([t.stringLiteral(ONLOOK_BABEL_PLUGIN)]) + t.arrayExpression([t.stringLiteral(ONLOOK_PLUGIN.BABEL)]) ) ]) ) @@ -148,7 +148,7 @@ export const modifyViteConfig = (configFileExtension: string): void => { t.objectExpression([ t.objectProperty( t.identifier('plugins'), - t.arrayExpression([t.stringLiteral(ONLOOK_BABEL_PLUGIN)]) + t.arrayExpression([t.stringLiteral(ONLOOK_PLUGIN.BABEL)]) ) ]) ) @@ -170,7 +170,7 @@ export const modifyViteConfig = (configFileExtension: string): void => { t.objectExpression([ t.objectProperty( t.identifier('plugins'), - t.arrayExpression([t.stringLiteral(ONLOOK_BABEL_PLUGIN)]) + t.arrayExpression([t.stringLiteral(ONLOOK_PLUGIN.BABEL)]) ) ]) ); @@ -185,14 +185,14 @@ export const modifyViteConfig = (configFileExtension: string): void => { if (!pluginsProp) { pluginsProp = t.objectProperty( t.identifier('plugins'), - t.arrayExpression([t.stringLiteral(ONLOOK_BABEL_PLUGIN)]) + t.arrayExpression([t.stringLiteral(ONLOOK_PLUGIN.BABEL)]) ); babelProp.value.properties.push(pluginsProp); reactPluginAdded = true; onlookBabelPluginAdded = true; } else if (t.isArrayExpression(pluginsProp.value)) { - if (!hasPlugin(pluginsProp.value.elements as t.Expression[], ONLOOK_BABEL_PLUGIN)) { - pluginsProp.value.elements.push(t.stringLiteral(ONLOOK_BABEL_PLUGIN)); + if (!hasPlugin(pluginsProp.value.elements as t.Expression[], ONLOOK_PLUGIN.BABEL)) { + pluginsProp.value.elements.push(t.stringLiteral(ONLOOK_PLUGIN.BABEL)); reactPluginAdded = true; onlookBabelPluginAdded = true; } @@ -213,7 +213,7 @@ export const modifyViteConfig = (configFileExtension: string): void => { console.log(`React plugin added to ${configFileName}`); } if (onlookBabelPluginAdded) { - console.log(`${ONLOOK_BABEL_PLUGIN} plugin added to ${configFileName}`); + console.log(`${ONLOOK_PLUGIN.BABEL} plugin added to ${configFileName}`); } if (reactImportAdded) { console.log(`React import added to ${configFileName}`); diff --git a/utils/src/frameworks/webpack.ts b/utils/src/frameworks/webpack.ts index be6af8662..e169f6cec 100644 --- a/utils/src/frameworks/webpack.ts +++ b/utils/src/frameworks/webpack.ts @@ -8,13 +8,13 @@ import * as path from 'path'; import { BABELRC_FILE, BUILD_TOOL_NAME, + CONFIG_BASE_NAME, CONFIG_FILE_PATTERN, DEPENDENCY_NAME, - ONLOOK_WEBPACK_PLUGIN, - WEBPACK_CONFIG_BASE_NAME -} from "./constants"; + ONLOOK_PLUGIN, +} from "../constants"; -import { exists, hasDependency, isSupportFileExtension } from "./utils"; +import { exists, hasDependency, isSupportFileExtension } from "../utils"; export const isWebpackProject = async (): Promise => { try { @@ -60,7 +60,7 @@ export function modifyWebpackConfig(configFileExtension: string): void { return; } - const configFileName = `${WEBPACK_CONFIG_BASE_NAME}${configFileExtension}`; + const configFileName = `${CONFIG_BASE_NAME.WEBPACK}${configFileExtension}`; // Define the path to webpack.config.* file const configPath = path.resolve(process.cwd(), configFileName); @@ -157,9 +157,9 @@ export const modifyBabelrc = (): void => { } // Check if "@onlook/react" is already in the plugins array - if (!babelrcContent.plugins.includes(ONLOOK_WEBPACK_PLUGIN)) { + if (!babelrcContent.plugins.includes(ONLOOK_PLUGIN.WEBPACK)) { // Add "@onlook/react" to the plugins array - babelrcContent.plugins.push(ONLOOK_WEBPACK_PLUGIN); + babelrcContent.plugins.push(ONLOOK_PLUGIN.WEBPACK); } // Write the updated content back to the .babelrc file diff --git a/utils/src/index.ts b/utils/src/index.ts index 5c6c7f0e5..ed23a5d89 100644 --- a/utils/src/index.ts +++ b/utils/src/index.ts @@ -1,5 +1,6 @@ export { createProject } from './create'; export { setupProject } from './setup'; +export { verifyProject } from './verify'; export enum CreateStage { CLONING = 'cloning', diff --git a/utils/src/utils.ts b/utils/src/utils.ts new file mode 100644 index 000000000..3c1d1a2f2 --- /dev/null +++ b/utils/src/utils.ts @@ -0,0 +1,134 @@ +import { NodePath } from '@babel/traverse'; +import * as t from '@babel/types'; +import { execSync } from 'child_process'; +import * as fs from 'fs'; +import * as glob from 'glob'; +import * as path from 'path'; +import { + FILE_EXTENSION, + LOCK_FILE_NAME, + PACKAGE_JSON, + PACKAGE_MANAGER, +} from './constants'; + +export const exists = async (filePattern: string): Promise => { + try { + const pattern = path.resolve(process.cwd(), filePattern); + const files = getFileNamesByPattern(pattern); + return files.length > 0; + } catch (err) { + console.error(err); + return false; + } +}; + +export const getFileNamesByPattern = (pattern: string): string[] => glob.globSync(pattern); + +export const installPackages = async (packages: string[]): Promise => { + const packageManager = await getPackageManager(); + const command = packageManager === PACKAGE_MANAGER.YARN ? 'yarn add -D' : `${packageManager} install -D`; + + console.log("Package manager found:", packageManager) + console.log("\n$", `${command} ${packages.join(' ')}`) + + execSync(`${command} ${packages.join(' ')}`, { stdio: 'inherit' }); +}; + +export const getPackageManager = async (): Promise => { + try { + if (await exists(LOCK_FILE_NAME.YARN)) { + return PACKAGE_MANAGER.YARN; + } + if (await exists(LOCK_FILE_NAME.PNPM)) { + return PACKAGE_MANAGER.PNPM; + } + if (await exists(LOCK_FILE_NAME.BUN)) { + return PACKAGE_MANAGER.BUN; + } + return PACKAGE_MANAGER.NPM; + } catch (e) { + console.error("Error determining package manager, using npm by default", e) + return PACKAGE_MANAGER.NPM + } + +}; + +export const hasDependency = async (dependencyName: string, targetPath?: string): Promise => { + const packageJsonPath = targetPath ? path.resolve(targetPath, PACKAGE_JSON) : path.resolve(PACKAGE_JSON); + + if (await exists(packageJsonPath)) { + const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf-8'); + const packageJson = JSON.parse(packageJsonContent); + return ( + (packageJson.dependencies && dependencyName in packageJson.dependencies) || + (packageJson.devDependencies && dependencyName in packageJson.devDependencies) + ); + } + return false; +}; + +export const getFileExtensionByPattern = async (dir: string, filePattern: string): Promise => { + const fullDirPattern = path.resolve(dir, filePattern); + const files = await getFileNamesByPattern(fullDirPattern); + + if (files.length > 0) { + return path.extname(files[0]); + } + + return null; +}; + +export const genASTParserOptionsByFileExtension = (fileExtension: string, sourceType: string = 'module'): object => { + switch (fileExtension) { + case FILE_EXTENSION.JS: + return { + sourceType: sourceType + }; + case FILE_EXTENSION.MJS: + return { + sourceType: sourceType, + plugins: ['jsx'] + }; + case FILE_EXTENSION.TS: + return { + sourceType: sourceType, + plugins: ['typescript'] + }; + default: + return {}; + } +}; + +export const genImportDeclaration = (fileExtension: string, dependency: string): t.VariableDeclaration | t.ImportDeclaration | null => { + switch (fileExtension) { + case FILE_EXTENSION.JS: + return t.variableDeclaration('const', [ + t.variableDeclarator( + t.identifier(dependency), + t.callExpression(t.identifier('require'), [t.stringLiteral(dependency)]) + ) + ]); + case FILE_EXTENSION.MJS: + return t.importDeclaration( + [t.importDefaultSpecifier(t.identifier(dependency))], + t.stringLiteral(dependency) + ); + default: + return null; + } +}; + +export const checkVariableDeclarationExist = (path: NodePath, dependency: string): boolean => { + return t.isIdentifier(path.node.id, { name: dependency }) && + t.isCallExpression(path.node.init) && + (path.node.init.callee as t.V8IntrinsicIdentifier).name === 'require' && + (path.node.init.arguments[0] as any).value === dependency; +}; + +export const isSupportFileExtension = (fileExtension: string): boolean => { + return [FILE_EXTENSION.JS, FILE_EXTENSION.MJS].indexOf(fileExtension as FILE_EXTENSION) !== -1; +}; + +export const isViteProjectSupportFileExtension = (fileExtension: string): boolean => { + return [FILE_EXTENSION.JS, FILE_EXTENSION.TS].indexOf(fileExtension as FILE_EXTENSION) !== -1; +}; diff --git a/utils/src/verify/index.ts b/utils/src/verify/index.ts index e69de29bb..ea001093b 100644 --- a/utils/src/verify/index.ts +++ b/utils/src/verify/index.ts @@ -0,0 +1,20 @@ +import { VerifyStage, type VerifyCallback } from '..'; +import { ONLOOK_PLUGIN } from '../constants'; +import { hasDependency } from '../utils'; + +export const verifyProject = async (targetPath: string, onProgress: VerifyCallback): Promise => { + try { + for (const dep of [ONLOOK_PLUGIN.BABEL, ONLOOK_PLUGIN.NEXTJS]) { + onProgress(VerifyStage.CHECKING, `Checking for ${dep}`); + if (await hasDependency(dep, targetPath)) { + onProgress(VerifyStage.COMPLETE, `Found ${dep}`); + return true; + } + } + onProgress(VerifyStage.COMPLETE, 'No Onlook dependencies found.'); + return false; + } catch (err) { + console.error(err); + return false; + } +}; From 06f143a36e141e23f272b0579b3cbaf2fb4ce2e6 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sat, 21 Sep 2024 19:36:33 -0400 Subject: [PATCH 25/40] Add setup calls --- app/bun.lockb | Bin 360866 -> 426114 bytes app/common/constants.ts | 2 ++ app/electron/main/events/create.ts | 14 +++++++++ app/package.json | 3 +- .../ProjectsTab/Create/Load/Verify.tsx | 29 +++++++++++++++--- app/vite.config.ts | 7 +++-- utils/src/frameworks/index.ts | 8 ++--- utils/src/setup/index.ts | 16 +++++++--- 8 files changed, 62 insertions(+), 17 deletions(-) diff --git a/app/bun.lockb b/app/bun.lockb index 300a12856688fab0b02158474a44b8ee3569b2ba..73e93c1a7ddf094b3159101f5bc04247e0dcc50c 100755 GIT binary patch delta 96234 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%0DK_?LFA6A$T^d{UteZc6lo|z$UZm9 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 delta 54682 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 diff --git a/app/common/constants.ts b/app/common/constants.ts index b26582887..82d2a5d1a 100644 --- a/app/common/constants.ts +++ b/app/common/constants.ts @@ -88,6 +88,8 @@ export enum MainChannels { CREATE_NEW_PROJECT_CALLBACK = 'create-new-project-callback', VERIFY_PROJECT = 'verify-project', VERIFY_PROJECT_CALLBACK = 'verify-project-callback', + SETUP_PROJECT = 'setup-project', + SETUP_PROJECT_CALLBACK = 'setup-project-callback', } export enum Links { diff --git a/app/electron/main/events/create.ts b/app/electron/main/events/create.ts index 71eff34a7..c2a353ae5 100644 --- a/app/electron/main/events/create.ts +++ b/app/electron/main/events/create.ts @@ -1,9 +1,12 @@ import { CreateCallback, CreateStage, + SetupCallback, + SetupStage, VerifyCallback, VerifyStage, createProject, + setupProject, verifyProject, } from '@onlook/utils'; import { ipcMain } from 'electron'; @@ -33,4 +36,15 @@ export function listenForCreateMessages() { const path = args as string; return verifyProject(path, progressCallback); }); + + ipcMain.handle(MainChannels.SETUP_PROJECT, (e: Electron.IpcMainInvokeEvent, args: string) => { + const progressCallback: SetupCallback = (stage: SetupStage, message: string) => { + mainWindow?.webContents.send(MainChannels.SETUP_PROJECT_CALLBACK, { + stage, + message, + }); + }; + const path = args as string; + return setupProject(path, progressCallback); + }); } diff --git a/app/package.json b/app/package.json index 530fdc031..66c9ec16e 100644 --- a/app/package.json +++ b/app/package.json @@ -88,9 +88,10 @@ "devDependencies": { "@eslint/compat": "^1.1.1", "@eslint/js": "^9.7.0", + "@onlook/babel-plugin-react": "^2.1.1", "@playwright/test": "^1.42.1", - "@types/babel__traverse": "^7.20.6", "@types/babel-generator": "^6.25.8", + "@types/babel__traverse": "^7.20.6", "@types/bun": "^1.1.6", "@types/css-tree": "^2.3.8", "@types/culori": "^2.1.0", diff --git a/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx b/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx index 685a3e08d..557589e77 100644 --- a/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx +++ b/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx @@ -20,8 +20,9 @@ export const LoadVerifyProject = ({ props: StepProps; }) => { const [isVerifying, setIsVerifying] = useState(true); + const [isInstalling, setIsInstalling] = useState(false); const [isInstalled, setIsInstalled] = useState(null); - const [verifyMessage, setVerifyMessage] = useState('Rummaging...'); + const [progressMessage, setProgressMessage] = useState('Starting...'); useEffect(() => { if (!projectData.folderPath) { @@ -36,7 +37,7 @@ export const LoadVerifyProject = ({ window.api.on( MainChannels.VERIFY_PROJECT_CALLBACK, ({ stage, message }: { stage: VerifyStage; message: string }) => { - setVerifyMessage(message); + setProgressMessage(message); if (stage === 'checking') { setIsVerifying(true); setIsInstalled(false); @@ -51,6 +52,26 @@ export const LoadVerifyProject = ({ }, []); async function installOnlook() { + setIsInstalling(true); + window.api + .invoke(MainChannels.SETUP_PROJECT, projectData.folderPath) + .then((isInstalled) => { + setIsInstalled(isInstalled as boolean); + }); + + window.api.on( + MainChannels.SETUP_PROJECT_CALLBACK, + ({ stage, message }: { stage: VerifyStage; message: string }) => { + setProgressMessage(message); + if (stage === 'checking') { + setIsVerifying(true); + setIsInstalled(false); + } else if (stage === 'complete') { + setIsVerifying(false); + } + }, + ); + setIsInstalled(true); } @@ -81,7 +102,7 @@ export const LoadVerifyProject = ({ {isVerifying ? (
-

{verifyMessage}

+

{progressMessage}

) : (
- {isVerifying ? ( + {isVerifying || isInstalling ? ( diff --git a/app/vite.config.ts b/app/vite.config.ts index e04a61c21..ef0fd2051 100644 --- a/app/vite.config.ts +++ b/app/vite.config.ts @@ -7,12 +7,13 @@ import pkg from './package.json'; // https://vitejs.dev/config/ export default defineConfig(({ command }) => { - rmSync('dist-electron', { recursive: true, force: true }); - + rmSync('dist-electron', { + recursive: true, + force: true, + }); const isServe = command === 'serve'; const isBuild = command === 'build'; const sourcemap = isServe || !!process.env.VSCODE_DEBUG; - return { resolve: { alias: { diff --git a/utils/src/frameworks/index.ts b/utils/src/frameworks/index.ts index a3aed5668..e6c37cca3 100644 --- a/utils/src/frameworks/index.ts +++ b/utils/src/frameworks/index.ts @@ -1,3 +1,4 @@ +import { SetupStage, type SetupCallback } from ".."; import { BUILD_TOOL_NAME, CONFIG_FILE_PATTERN, CRA_DEPENDENCIES, NEXT_DEPENDENCIES, VITE_DEPENDENCIES, WEBPACK_DEPENDENCIES } from "../constants"; import { getFileExtensionByPattern, installPackages } from "../utils"; import { isCRAProject, modifyCRAConfig } from "./cra"; @@ -5,7 +6,6 @@ import { isNextJsProject, modifyNextConfig } from "./next"; import { isViteJsProject, modifyViteConfig } from "./vite"; import { isWebpackProject, modifyWebpackConfig } from "./webpack"; - export class Framework { static readonly NEXT = new Framework("Next.js", isNextJsProject, modifyNextConfig, NEXT_DEPENDENCIES, BUILD_TOOL_NAME.NEXT); static readonly VITE = new Framework("Vite", isViteJsProject, modifyViteConfig, VITE_DEPENDENCIES, BUILD_TOOL_NAME.VITE); @@ -20,12 +20,12 @@ export class Framework { public readonly buildToolName: BUILD_TOOL_NAME ) { } - setup = async (): Promise => { + setup = async (callback: SetupCallback): Promise => { if (await this.identify()) { - console.log(`This is a ${this.name} project.`); - + callback(SetupStage.INSTALLING, `Installing required packages for ${this.name}...`); await installPackages(this.dependencies); + callback(SetupStage.CONFIGURING, `Applying ${this.name} configuration...`); const configFileExtension = await getFileExtensionByPattern(process.cwd(), CONFIG_FILE_PATTERN[this.buildToolName]); if (configFileExtension) { await this.updateConfig(configFileExtension); diff --git a/utils/src/setup/index.ts b/utils/src/setup/index.ts index 4ddeadcfb..48b088f67 100755 --- a/utils/src/setup/index.ts +++ b/utils/src/setup/index.ts @@ -1,16 +1,22 @@ -import type { SetupCallback } from '..'; +import { SetupStage, type SetupCallback } from '..'; import { Framework } from '../frameworks'; -export const setupProject = async (targetPath: string, onProgress: SetupCallback): Promise => { +export const setupProject = async (targetPath: string, onProgress: SetupCallback): Promise => { try { + onProgress(SetupStage.INSTALLING, 'Installing required packages...'); + for (const framework of Framework.getAll()) { - const updated = await framework.setup(); + onProgress(SetupStage.INSTALLING, 'Checking for' + framework.name + ' configuration...'); + const updated = await framework.setup(onProgress); if (updated) { - return; + onProgress(SetupStage.COMPLETE, 'Project setup complete.'); + return true; } } - console.warn('Cannot determine the project framework.', '\nIf this is unexpected, see: https://github.com/onlook-dev/onlook/wiki/How-to-set-up-my-project%3F#do-it-manually'); + console.error('Cannot determine the project framework.', '\nIf this is unexpected, see: https://github.com/onlook-dev/onlook/wiki/How-to-set-up-my-project%3F#do-it-manually'); + return false; } catch (err) { console.error(err); + return false; } }; From ffe048f7b30d984c764dd7203a927f6efea6f648 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sat, 21 Sep 2024 19:39:01 -0400 Subject: [PATCH 26/40] Handle path --- utils/src/frameworks/index.ts | 6 +++++- utils/src/setup/index.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/utils/src/frameworks/index.ts b/utils/src/frameworks/index.ts index e6c37cca3..60311ad4d 100644 --- a/utils/src/frameworks/index.ts +++ b/utils/src/frameworks/index.ts @@ -1,3 +1,4 @@ +import { chdir } from 'process'; import { SetupStage, type SetupCallback } from ".."; import { BUILD_TOOL_NAME, CONFIG_FILE_PATTERN, CRA_DEPENDENCIES, NEXT_DEPENDENCIES, VITE_DEPENDENCIES, WEBPACK_DEPENDENCIES } from "../constants"; import { getFileExtensionByPattern, installPackages } from "../utils"; @@ -20,9 +21,12 @@ export class Framework { public readonly buildToolName: BUILD_TOOL_NAME ) { } - setup = async (callback: SetupCallback): Promise => { + setup = async (targetPath: string, callback: SetupCallback): Promise => { + chdir(targetPath); + if (await this.identify()) { callback(SetupStage.INSTALLING, `Installing required packages for ${this.name}...`); + await installPackages(this.dependencies); callback(SetupStage.CONFIGURING, `Applying ${this.name} configuration...`); diff --git a/utils/src/setup/index.ts b/utils/src/setup/index.ts index 48b088f67..ae455bf35 100755 --- a/utils/src/setup/index.ts +++ b/utils/src/setup/index.ts @@ -7,7 +7,7 @@ export const setupProject = async (targetPath: string, onProgress: SetupCallback for (const framework of Framework.getAll()) { onProgress(SetupStage.INSTALLING, 'Checking for' + framework.name + ' configuration...'); - const updated = await framework.setup(onProgress); + const updated = await framework.setup(targetPath, onProgress); if (updated) { onProgress(SetupStage.COMPLETE, 'Project setup complete.'); return true; From 234afe50e9daacc4ca2fad76806fe026cfa66597 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sat, 21 Sep 2024 20:03:34 -0400 Subject: [PATCH 27/40] Working install --- .../ProjectsTab/Create/Load/Verify.tsx | 61 +++++++------ .../projects/ProjectsTab/Create/index.tsx | 4 +- bun.lockb | Bin 1233 -> 81434 bytes docs/bun.lockb | Bin 193468 -> 193468 bytes docs/next.config.mjs | 18 ++-- docs/package.json | 84 +++++++++--------- package.json | 46 +++++----- utils/src/frameworks/frameworks.ts | 2 + utils/src/frameworks/index.ts | 4 +- utils/src/setup/index.ts | 3 +- 10 files changed, 117 insertions(+), 105 deletions(-) diff --git a/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx b/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx index 557589e77..93be698c2 100644 --- a/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx +++ b/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx @@ -56,6 +56,7 @@ export const LoadVerifyProject = ({ window.api .invoke(MainChannels.SETUP_PROJECT, projectData.folderPath) .then((isInstalled) => { + setIsInstalling(false); setIsInstalled(isInstalled as boolean); }); @@ -71,8 +72,6 @@ export const LoadVerifyProject = ({ } }, ); - - setIsInstalled(true); } function handleSelectDifferentFolder() { @@ -80,6 +79,38 @@ export const LoadVerifyProject = ({ prevStep(); } + function renderMainContent() { + if (isVerifying || isInstalling) { + return ( +
+ +

{progressMessage}

+
+ ); + } + + return ( +
+
+

{projectData.name}

+

{projectData.folderPath}

+
+ {isInstalled ? ( + + ) : ( + + )} +
+ ); + } + return ( @@ -99,31 +130,7 @@ export const LoadVerifyProject = ({ - {isVerifying ? ( -
- -

{progressMessage}

-
- ) : ( -
-
-

{projectData.name}

-

{projectData.folderPath}

-
- {isInstalled ? ( - - ) : ( - - )} -
- )} + {renderMainContent()}

{`${currentStep + 1} of ${totalSteps}`}

diff --git a/app/src/routes/projects/ProjectsTab/Create/index.tsx b/app/src/routes/projects/ProjectsTab/Create/index.tsx index bbee1f61e..cf474e99a 100644 --- a/app/src/routes/projects/ProjectsTab/Create/index.tsx +++ b/app/src/routes/projects/ProjectsTab/Create/index.tsx @@ -33,7 +33,9 @@ const CreateProject = ({ const TOTAL_LOAD_STEPS = 4; const [currentStep, setCurrentStep] = useState(0); const [totalSteps, setTotalSteps] = useState(0); - const [projectData, setProjectData] = useState>({}); + const [projectData, setProjectData] = useState>({ + url: 'http://localhost:3000', + }); useEffect(() => { setCurrentStep(0); diff --git a/bun.lockb b/bun.lockb index 59dd362526d91d1a0efe65a812bc44f1ad28c23f..aa33e133e4cb5d4d390a9497aa7ad2a912321b1c 100755 GIT binary patch literal 81434 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 B36%f< delta 283 zcmbRBhvg#Y1U=1VzHfN^PHUgocqsbSto08k=kKjnagmeczVq~v$L*BA?v5-BV8Apn zTwbe+3BrMpd<+Z?8KuS9l|b&dBXa|fH{Y)if0t}+{chW`$qC#ZjC_+*QD*b0Gtg|2$s3;QaQ}b` aS+X!pe*Rp4^2ZM{lYL%@Pj9kioB;rt?N!_W diff --git a/docs/bun.lockb b/docs/bun.lockb index 024575ca754b8d448b83dae7e0e650badf166945..f6656f3038d6c0b17fa9c78054a808b5ff4d6430 100755 GIT binary patch delta 34 ncmdnq+`jn@6DK => { + console.log(process.cwd()); + console.log(`Checking for ${this.name} configuration...`); if (await this.identify()) { console.log(`This is a ${this.name} project.`); diff --git a/utils/src/frameworks/index.ts b/utils/src/frameworks/index.ts index 60311ad4d..9443518c5 100644 --- a/utils/src/frameworks/index.ts +++ b/utils/src/frameworks/index.ts @@ -1,4 +1,3 @@ -import { chdir } from 'process'; import { SetupStage, type SetupCallback } from ".."; import { BUILD_TOOL_NAME, CONFIG_FILE_PATTERN, CRA_DEPENDENCIES, NEXT_DEPENDENCIES, VITE_DEPENDENCIES, WEBPACK_DEPENDENCIES } from "../constants"; import { getFileExtensionByPattern, installPackages } from "../utils"; @@ -21,8 +20,7 @@ export class Framework { public readonly buildToolName: BUILD_TOOL_NAME ) { } - setup = async (targetPath: string, callback: SetupCallback): Promise => { - chdir(targetPath); + setup = async (callback: SetupCallback): Promise => { if (await this.identify()) { callback(SetupStage.INSTALLING, `Installing required packages for ${this.name}...`); diff --git a/utils/src/setup/index.ts b/utils/src/setup/index.ts index ae455bf35..6818dd901 100755 --- a/utils/src/setup/index.ts +++ b/utils/src/setup/index.ts @@ -3,11 +3,12 @@ import { Framework } from '../frameworks'; export const setupProject = async (targetPath: string, onProgress: SetupCallback): Promise => { try { + process.chdir(targetPath); onProgress(SetupStage.INSTALLING, 'Installing required packages...'); for (const framework of Framework.getAll()) { onProgress(SetupStage.INSTALLING, 'Checking for' + framework.name + ' configuration...'); - const updated = await framework.setup(targetPath, onProgress); + const updated = await framework.setup(onProgress); if (updated) { onProgress(SetupStage.COMPLETE, 'Project setup complete.'); return true; From 90d5c9c0a954676ff2a2fdbb3a0d9ff4c8e79045 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sat, 21 Sep 2024 20:09:45 -0400 Subject: [PATCH 28/40] Handle install failed --- .../projects/ProjectsTab/Create/Load/Verify.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx b/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx index 93be698c2..c71668fd4 100644 --- a/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx +++ b/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx @@ -7,6 +7,7 @@ import { CardHeader, CardTitle, } from '@/components/ui/card'; +import { toast } from '@/components/ui/use-toast'; import { VerifyStage } from '@onlook/utils'; import { CheckCircledIcon, ExclamationTriangleIcon, ShadowIcon } from '@radix-ui/react-icons'; import clsx from 'clsx'; @@ -23,6 +24,7 @@ export const LoadVerifyProject = ({ const [isInstalling, setIsInstalling] = useState(false); const [isInstalled, setIsInstalled] = useState(null); const [progressMessage, setProgressMessage] = useState('Starting...'); + const [errorMessage, setErrorMessage] = useState(null); useEffect(() => { if (!projectData.folderPath) { @@ -57,7 +59,14 @@ export const LoadVerifyProject = ({ .invoke(MainChannels.SETUP_PROJECT, projectData.folderPath) .then((isInstalled) => { setIsInstalling(false); - setIsInstalled(isInstalled as boolean); + if (isInstalled === true) { + setIsInstalled(true); + } else { + toast({ + title: 'Error installing Onlook', + description: 'Please try again or contact support', + }); + } }); window.api.on( From 3f1580b3f90d0879e818a6998d1130dbb1c760b3 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sat, 21 Sep 2024 20:17:01 -0400 Subject: [PATCH 29/40] Clean up --- app/bun.lockb | Bin 426114 -> 426171 bytes app/package.json | 1 + cli/bun.lockb | Bin 47340 -> 34405 bytes cli/package.json | 8 +------- 4 files changed, 2 insertions(+), 7 deletions(-) diff --git a/app/bun.lockb b/app/bun.lockb index 73e93c1a7ddf094b3159101f5bc04247e0dcc50c..b1eb2c979490ab9bf83ab9a1f6640adccd08c748 100755 GIT binary patch delta 78665 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 z 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

BeWL3 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 diff --git a/app/package.json b/app/package.json index 66c9ec16e..7558026e7 100644 --- a/app/package.json +++ b/app/package.json @@ -36,6 +36,7 @@ "remove_tag": "./scripts/remove_tag.sh" }, "dependencies": { + "@onlook/utils": "^0.0.0", "@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-context-menu": "^2.2.1", diff --git a/cli/bun.lockb b/cli/bun.lockb index b2e51cdb3ff27c72ec969c8d6565f9b907c77567..bf1d14419a016c9f00de5f03ff62b355a7b2e459 100755 GIT binary patch delta 6499 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 Date: Sat, 21 Sep 2024 20:41:03 -0400 Subject: [PATCH 30/40] Working @onlook/utils --- app/bun.lockb | Bin 426171 -> 426171 bytes docs/next.config.mjs | 12 ++----- docs/package.json | 83 +++++++++++++++++++++---------------------- utils/package.json | 6 ++-- 4 files changed, 47 insertions(+), 54 deletions(-) diff --git a/app/bun.lockb b/app/bun.lockb index b1eb2c979490ab9bf83ab9a1f6640adccd08c748..6e768266fc01272481671556d99957e4b966970a 100755 GIT binary patch delta 39 rcmdnpD7Cv$s-cCkg{g&k3rnvqJ7b)I9uT%q(PaT**6mYt+0=sp1NsaU delta 39 pcmdnpD7Cv$s-cCkg{g&k3rnvqI}-yKv`^7x0b Date: Sat, 21 Sep 2024 21:22:37 -0400 Subject: [PATCH 31/40] Need to use callback for success and error --- app/bun.lockb | Bin 426171 -> 426171 bytes app/package.json | 2 +- .../ProjectsTab/Create/Load/Verify.tsx | 56 ++++++++++-------- docs/next.config.mjs | 10 +++- docs/package.json | 1 + utils/README.md | 5 +- utils/package.json | 2 +- 7 files changed, 43 insertions(+), 33 deletions(-) diff --git a/app/bun.lockb b/app/bun.lockb index 6e768266fc01272481671556d99957e4b966970a..4a8ce1db4c7151e70a7fd97114e3f728b0e9d008 100755 GIT binary patch delta 166 zcmV;X09pULff~Dk8jvm^qF85{sOJ}33Iv%a0V@P$?`lJe3)-&+wBn4B?oMBRu}<2N zljuP(gIJQcSdsw^T0r=MIx&m*D8tUI7rpG;Z?R>;ku?>;vdP2Lb>900000hmF;==KeKofu}<2N zm(17#D3g%(34>XZw^@<_3|c@LhGAdu3s!$5qVuG6uly-}h(YI63{6B~NbqhqB!pz< z8+hp$w3@rz6%!h644#E5i`4IN^Cky74kY@IkIKD=rR)KRrR)N?rR)RfKnDR{FfK4I ZFo%>a1h { - const [isVerifying, setIsVerifying] = useState(true); - const [isInstalling, setIsInstalling] = useState(false); - const [isInstalled, setIsInstalled] = useState(null); + const [state, setState] = useState(VerifyState.VERIFYING); const [progressMessage, setProgressMessage] = useState('Starting...'); - const [errorMessage, setErrorMessage] = useState(null); useEffect(() => { if (!projectData.folderPath) { @@ -33,7 +38,11 @@ export const LoadVerifyProject = ({ window.api .invoke(MainChannels.VERIFY_PROJECT, projectData.folderPath) .then((isInstalled) => { - setIsInstalled(isInstalled as boolean); + if (isInstalled === true) { + setState(VerifyState.INSTALLED); + } else { + setState(VerifyState.NOT_INSTALLED); + } }); window.api.on( @@ -41,31 +50,29 @@ export const LoadVerifyProject = ({ ({ stage, message }: { stage: VerifyStage; message: string }) => { setProgressMessage(message); if (stage === 'checking') { - setIsVerifying(true); - setIsInstalled(false); - } else if (stage === 'complete') { - setIsVerifying(false); + setState(VerifyState.VERIFYING); } }, ); return () => { window.api.removeAllListeners(MainChannels.VERIFY_PROJECT_CALLBACK); }; - }, []); + }, [projectData.folderPath]); async function installOnlook() { - setIsInstalling(true); + setState(VerifyState.INSTALLING); + window.api .invoke(MainChannels.SETUP_PROJECT, projectData.folderPath) .then((isInstalled) => { - setIsInstalling(false); if (isInstalled === true) { - setIsInstalled(true); + setState(VerifyState.INSTALLED); } else { toast({ title: 'Error installing Onlook', description: 'Please try again or contact support', }); + setState(VerifyState.ERROR); } }); @@ -74,10 +81,7 @@ export const LoadVerifyProject = ({ ({ stage, message }: { stage: VerifyStage; message: string }) => { setProgressMessage(message); if (stage === 'checking') { - setIsVerifying(true); - setIsInstalled(false); - } else if (stage === 'complete') { - setIsVerifying(false); + setState(VerifyState.INSTALLING); } }, ); @@ -89,7 +93,7 @@ export const LoadVerifyProject = ({ } function renderMainContent() { - if (isVerifying || isInstalling) { + if (state === VerifyState.INSTALLING || state === VerifyState.VERIFYING) { return (

@@ -102,7 +106,7 @@ export const LoadVerifyProject = ({
{projectData.name}

{projectData.folderPath}

- {isInstalled ? ( + {state === VerifyState.INSTALLED ? ( ) : ( @@ -124,16 +128,16 @@ export const LoadVerifyProject = ({ - {isVerifying + {state === VerifyState.VERIFYING ? 'Verifying project...' - : isInstalled + : state === VerifyState.INSTALLED ? 'Onlook is installed' : 'Onlook is not installed'} - {isVerifying + {state === VerifyState.VERIFYING ? 'Checking your dependencies and configurations' - : isInstalled + : state === VerifyState.INSTALLED ? 'Your project is all set up' : 'It takes one second to install Onlook on your project'} @@ -148,11 +152,11 @@ export const LoadVerifyProject = ({ Select a different folder - {isVerifying || isInstalling ? ( + {state === VerifyState.INSTALLING || state === VerifyState.VERIFYING ? ( - ) : isInstalled ? ( + ) : state === VerifyState.INSTALLED ? ( diff --git a/docs/next.config.mjs b/docs/next.config.mjs index bef38dd33..eb7566655 100644 --- a/docs/next.config.mjs +++ b/docs/next.config.mjs @@ -1,6 +1,10 @@ +import path from "path"; const nextConfig = { - reactStrictMode: true, - experimental: { - } + reactStrictMode: true, + experimental: { + swcPlugins: [["@onlook/nextjs", { + root: path.resolve(".") + }]] + } }; export default nextConfig; \ No newline at end of file diff --git a/docs/package.json b/docs/package.json index 64fb701d0..a703373b1 100644 --- a/docs/package.json +++ b/docs/package.json @@ -28,6 +28,7 @@ }, "devDependencies": { "@ianvs/prettier-plugin-sort-imports": "^4.2.1", + "@onlook/nextjs": "^2.1.1", "@types/node": "^20.12.7", "@types/react": "^18.3.1", "@types/react-dom": "^18.3.0", diff --git a/utils/README.md b/utils/README.md index 715562f4e..44cc9d437 100644 --- a/utils/README.md +++ b/utils/README.md @@ -2,5 +2,6 @@ A shared utility package for Onlook. Includes file system functionalities used in both the Onlook app and the CLI -[X] Create new project -[ ] Setup existing project \ No newline at end of file +- [X] Create new project +- [X] Setup existing project +- [X] Verify Onlook installation \ No newline at end of file diff --git a/utils/package.json b/utils/package.json index d23f28068..7e8a9c3ac 100644 --- a/utils/package.json +++ b/utils/package.json @@ -1,7 +1,7 @@ { "name": "@onlook/utils", "description": "A shared utility library for Onlook", - "version": "0.0.0", + "version": "0.0.2", "type": "commonjs", "main": "dist/index.js", "module": "dist/index.mjs", From 61a8f1357279dd7e76feb4ee5917538967e0dbf2 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sat, 21 Sep 2024 21:29:09 -0400 Subject: [PATCH 32/40] Should handle new states --- .../routes/projects/ProjectsTab/Create/Load/Verify.tsx | 10 +--------- utils/src/index.ts | 3 ++- utils/src/setup/index.ts | 8 ++++---- utils/src/verify/index.ts | 10 ++++------ 4 files changed, 11 insertions(+), 20 deletions(-) diff --git a/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx b/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx index 9da162e96..efb17b205 100644 --- a/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx +++ b/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx @@ -35,15 +35,7 @@ export const LoadVerifyProject = ({ if (!projectData.folderPath) { throw new Error('Folder path is not provided'); } - window.api - .invoke(MainChannels.VERIFY_PROJECT, projectData.folderPath) - .then((isInstalled) => { - if (isInstalled === true) { - setState(VerifyState.INSTALLED); - } else { - setState(VerifyState.NOT_INSTALLED); - } - }); + window.api.invoke(MainChannels.VERIFY_PROJECT, projectData.folderPath); window.api.on( MainChannels.VERIFY_PROJECT_CALLBACK, diff --git a/utils/src/index.ts b/utils/src/index.ts index ed23a5d89..c23aba96f 100644 --- a/utils/src/index.ts +++ b/utils/src/index.ts @@ -11,7 +11,8 @@ export enum CreateStage { export enum VerifyStage { CHECKING = 'checking', - COMPLETE = 'complete', + NOT_INSTALLED = 'not_installed', + INSTALLED = 'installed', ERROR = 'error' } diff --git a/utils/src/setup/index.ts b/utils/src/setup/index.ts index 6818dd901..62e9d565e 100755 --- a/utils/src/setup/index.ts +++ b/utils/src/setup/index.ts @@ -1,7 +1,7 @@ import { SetupStage, type SetupCallback } from '..'; import { Framework } from '../frameworks'; -export const setupProject = async (targetPath: string, onProgress: SetupCallback): Promise => { +export const setupProject = async (targetPath: string, onProgress: SetupCallback): Promise => { try { process.chdir(targetPath); onProgress(SetupStage.INSTALLING, 'Installing required packages...'); @@ -11,13 +11,13 @@ export const setupProject = async (targetPath: string, onProgress: SetupCallback const updated = await framework.setup(onProgress); if (updated) { onProgress(SetupStage.COMPLETE, 'Project setup complete.'); - return true; + return; } } console.error('Cannot determine the project framework.', '\nIf this is unexpected, see: https://github.com/onlook-dev/onlook/wiki/How-to-set-up-my-project%3F#do-it-manually'); - return false; + onProgress(SetupStage.ERROR, 'Project setup failed.'); } catch (err) { console.error(err); - return false; + onProgress(SetupStage.ERROR, 'An error occurred.'); } }; diff --git a/utils/src/verify/index.ts b/utils/src/verify/index.ts index ea001093b..5be256b0f 100644 --- a/utils/src/verify/index.ts +++ b/utils/src/verify/index.ts @@ -2,19 +2,17 @@ import { VerifyStage, type VerifyCallback } from '..'; import { ONLOOK_PLUGIN } from '../constants'; import { hasDependency } from '../utils'; -export const verifyProject = async (targetPath: string, onProgress: VerifyCallback): Promise => { +export const verifyProject = async (targetPath: string, onProgress: VerifyCallback): Promise => { try { for (const dep of [ONLOOK_PLUGIN.BABEL, ONLOOK_PLUGIN.NEXTJS]) { onProgress(VerifyStage.CHECKING, `Checking for ${dep}`); if (await hasDependency(dep, targetPath)) { - onProgress(VerifyStage.COMPLETE, `Found ${dep}`); - return true; + onProgress(VerifyStage.INSTALLED, `Found ${dep}`); + return; } } - onProgress(VerifyStage.COMPLETE, 'No Onlook dependencies found.'); - return false; + onProgress(VerifyStage.NOT_INSTALLED, 'No Onlook dependencies found.'); } catch (err) { console.error(err); - return false; } }; From 9486fe7b2082044f8c851f2c6304aab639380f31 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sat, 21 Sep 2024 21:31:01 -0400 Subject: [PATCH 33/40] Add states --- app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx b/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx index efb17b205..771bcab1c 100644 --- a/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx +++ b/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx @@ -35,6 +35,8 @@ export const LoadVerifyProject = ({ if (!projectData.folderPath) { throw new Error('Folder path is not provided'); } + + // TODO: This should be called at the end of the last stage window.api.invoke(MainChannels.VERIFY_PROJECT, projectData.folderPath); window.api.on( @@ -43,6 +45,10 @@ export const LoadVerifyProject = ({ setProgressMessage(message); if (stage === 'checking') { setState(VerifyState.VERIFYING); + } else if (stage === 'not_installed') { + setState(VerifyState.NOT_INSTALLED); + } else if (stage === 'installed') { + setState(VerifyState.INSTALLED); } }, ); From 91213ec918f29a50bf1af486b2a1d62f4e0266e2 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 22 Sep 2024 10:01:33 -0400 Subject: [PATCH 34/40] Better verify state --- .vscode/.debug.script.mjs | 13 ++--- .vscode/tasks.json | 55 +++++++++---------- .../ProjectsTab/Create/Load/SelectFolder.tsx | 7 ++- .../ProjectsTab/Create/Load/Verify.tsx | 4 -- utils/src/verify/index.ts | 5 +- 5 files changed, 42 insertions(+), 42 deletions(-) diff --git a/.vscode/.debug.script.mjs b/.vscode/.debug.script.mjs index 9ca93363c..a641e3186 100644 --- a/.vscode/.debug.script.mjs +++ b/.vscode/.debug.script.mjs @@ -13,11 +13,10 @@ fs.writeFileSync(path.join(__dirname, '.debug.env'), envContent.join('\n')) // bootstrap spawn( - // TODO: terminate `npm run dev` when Debug exits. - process.platform === 'win32' ? 'npm.cmd' : 'npm', - ['run', 'dev'], - { - stdio: 'inherit', - env: Object.assign(process.env, { VSCODE_DEBUG: 'true' }), - }, + process.platform === 'win32' ? 'npm.cmd' : 'npm', + ['run', 'dev'], + { + stdio: 'inherit', + env: Object.assign(process.env, { VSCODE_DEBUG: 'true' }), + }, ) \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 85d09cdea..0b7825179 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,31 +1,30 @@ { - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "2.0.0", - "tasks": [ - { - "label": "Before Debug", - "type": "shell", - "command": "node .vscode/.debug.script.mjs", - "isBackground": true, - "problemMatcher": { - "owner": "typescript", - "fileLocation": "relative", - "pattern": { - // TODO: correct "regexp" - "regexp": "^([a-zA-Z]\\:\/?([\\w\\-]\/?)+\\.\\w+):(\\d+):(\\d+): (ERROR|WARNING)\\: (.*)$", - "file": 1, - "line": 3, - "column": 4, - "code": 5, - "message": 6 - }, - "background": { - "activeOnStart": true, - "beginsPattern": "^.*VITE v.* ready in \\d* ms.*$", - "endsPattern": "^.*\\[startup\\] Electron App.*$" + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Before Debug", + "type": "shell", + "command": "node .vscode/.debug.script.mjs", + "isBackground": true, + "problemMatcher": { + "owner": "typescript", + "fileLocation": "relative", + "pattern": { + "regexp": "^([a-zA-Z]\\:\/?([\\w\\-]\/?)+\\.\\w+):(\\d+):(\\d+): (ERROR|WARNING)\\: (.*)$", + "file": 1, + "line": 3, + "column": 4, + "code": 5, + "message": 6 + }, + "background": { + "activeOnStart": true, + "beginsPattern": "^.*VITE v.* ready in \\d* ms.*$", + "endsPattern": "^.*\\[startup\\] Electron App.*$" + } + } } - } - } - ] + ] } \ No newline at end of file diff --git a/app/src/routes/projects/ProjectsTab/Create/Load/SelectFolder.tsx b/app/src/routes/projects/ProjectsTab/Create/Load/SelectFolder.tsx index 9bab8792f..3d10baa01 100644 --- a/app/src/routes/projects/ProjectsTab/Create/Load/SelectFolder.tsx +++ b/app/src/routes/projects/ProjectsTab/Create/Load/SelectFolder.tsx @@ -35,6 +35,11 @@ export const LoadSelectFolder = ({ }); } + function verifyFolder() { + window.api.invoke(MainChannels.VERIFY_PROJECT, projectData.folderPath); + nextStep(); + } + return ( @@ -81,7 +86,7 @@ export const LoadSelectFolder = ({ + ); + } else if (state === StepState.INSTALLED) { + return ( + + ); + } else if (state === StepState.NOT_INSTALLED) { + return ( + + ); + } + } + return ( - - {state === VerifyState.VERIFYING - ? 'Verifying project...' - : state === VerifyState.INSTALLED - ? 'Onlook is installed' - : 'Onlook is not installed'} - - - {state === VerifyState.VERIFYING - ? 'Checking your dependencies and configurations' - : state === VerifyState.INSTALLED - ? 'Your project is all set up' - : 'It takes one second to install Onlook on your project'} - + {renderTitle()} + {renderDescription()} {renderMainContent()} @@ -145,20 +202,7 @@ export const LoadVerifyProject = ({ - - {state === VerifyState.INSTALLING || state === VerifyState.VERIFYING ? ( - - ) : state === VerifyState.INSTALLED ? ( - - ) : ( - - )} + {renderPrimaryButton()}
From 80c60ce1e27bd4f063a89da0c69a1cccc6e282f0 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 22 Sep 2024 10:42:43 -0400 Subject: [PATCH 36/40] Working configuration --- .../ProjectsTab/Create/Load/Verify.tsx | 34 +++----- docs/package.json | 84 +++++++++---------- 2 files changed, 52 insertions(+), 66 deletions(-) diff --git a/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx b/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx index 46eb7aa44..2bbed90e9 100644 --- a/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx +++ b/app/src/routes/projects/ProjectsTab/Create/Load/Verify.tsx @@ -7,7 +7,6 @@ import { CardHeader, CardTitle, } from '@/components/ui/card'; -import { toast } from '@/components/ui/use-toast'; import type { SetupStage, VerifyStage } from '@onlook/utils'; import { CheckCircledIcon, ExclamationTriangleIcon, ShadowIcon } from '@radix-ui/react-icons'; import clsx from 'clsx'; @@ -50,29 +49,6 @@ export const LoadVerifyProject = ({ } }, ); - return () => { - window.api.removeAllListeners(MainChannels.VERIFY_PROJECT_CALLBACK); - }; - }, [projectData.folderPath]); - - async function installOnlook() { - setState(StepState.INSTALLING); - - window.api - .invoke(MainChannels.SETUP_PROJECT, projectData.folderPath) - .then((isInstalled) => { - if (isInstalled === true) { - setState(StepState.INSTALLED); - } else { - toast({ - title: 'Error installing Onlook', - description: 'Please try again or contact support', - }); - setProgressMessage('Please try again or contact support'); - setState(StepState.ERROR); - } - }); - window.api.on( MainChannels.SETUP_PROJECT_CALLBACK, ({ stage, message }: { stage: SetupStage; message: string }) => { @@ -86,6 +62,16 @@ export const LoadVerifyProject = ({ } }, ); + return () => { + window.api.removeAllListeners(MainChannels.VERIFY_PROJECT_CALLBACK); + window.api.removeAllListeners(MainChannels.SETUP_PROJECT_CALLBACK); + }; + }, [projectData.folderPath]); + + async function installOnlook() { + setState(StepState.INSTALLING); + + window.api.invoke(MainChannels.SETUP_PROJECT, projectData.folderPath); } function handleSelectDifferentFolder() { diff --git a/docs/package.json b/docs/package.json index a703373b1..2e63dcb63 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,44 +1,44 @@ { - "name": "@onlook/docs", - "version": "0.0.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "next lint", - "format": "prettier ./src --write", - "format:check": "prettier ./src --check" - }, - "dependencies": { - "@radix-ui/react-dropdown-menu": "^2.0.6", - "@radix-ui/react-slot": "^1.0.2", - "@t3-oss/env-nextjs": "^0.9.2", - "class-variance-authority": "^0.7.0", - "clsx": "^2.1.1", - "lucide-react": "^0.330.0", - "next": "14.2.10", - "next-entree": ".", - "next-themes": "^0.2.1", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "tailwind-merge": "^2.3.0", - "tailwindcss-animate": "^1.0.7", - "zod": "^3.23.4" - }, - "devDependencies": { - "@ianvs/prettier-plugin-sort-imports": "^4.2.1", - "@onlook/nextjs": "^2.1.1", - "@types/node": "^20.12.7", - "@types/react": "^18.3.1", - "@types/react-dom": "^18.3.0", - "autoprefixer": "^10.4.19", - "eslint": "^8.57.0", - "eslint-config-next": "14.1.0", - "postcss": "^8.4.38", - "prettier": "^3.2.5", - "prettier-plugin-tailwindcss": "^0.5.14", - "tailwindcss": "^3.4.3", - "typescript": "^5.4.5" - } + "name": "@onlook/docs", + "version": "0.0.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "format": "prettier ./src --write", + "format:check": "prettier ./src --check" + }, + "dependencies": { + "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-slot": "^1.0.2", + "@t3-oss/env-nextjs": "^0.9.2", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "lucide-react": "^0.330.0", + "next": "14.2.10", + "next-entree": ".", + "next-themes": "^0.2.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "tailwind-merge": "^2.3.0", + "tailwindcss-animate": "^1.0.7", + "zod": "^3.23.4" + }, + "devDependencies": { + "@ianvs/prettier-plugin-sort-imports": "^4.2.1", + "@onlook/nextjs": "^2.1.1", + "@types/node": "^20.12.7", + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.0", + "autoprefixer": "^10.4.19", + "eslint": "^8.57.0", + "eslint-config-next": "14.1.0", + "postcss": "^8.4.38", + "prettier": "^3.2.5", + "prettier-plugin-tailwindcss": "^0.5.14", + "tailwindcss": "^3.4.3", + "typescript": "^5.4.5" + } } \ No newline at end of file From c5cd21c041037070d0614456db50df6d8eb836df Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 22 Sep 2024 10:44:56 -0400 Subject: [PATCH 37/40] Update utils package --- app/package.json | 3 +-- utils/package.json | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/package.json b/app/package.json index 03c53035c..e594b908b 100644 --- a/app/package.json +++ b/app/package.json @@ -36,7 +36,7 @@ "remove_tag": "./scripts/remove_tag.sh" }, "dependencies": { - "@onlook/utils": "^0.0.2", + "@onlook/utils": "^0.0.3", "@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-context-menu": "^2.2.1", @@ -89,7 +89,6 @@ "devDependencies": { "@eslint/compat": "^1.1.1", "@eslint/js": "^9.7.0", - "@onlook/babel-plugin-react": "^2.1.1", "@playwright/test": "^1.42.1", "@types/babel-generator": "^6.25.8", "@types/babel__traverse": "^7.20.6", diff --git a/utils/package.json b/utils/package.json index 7e8a9c3ac..78ff99772 100644 --- a/utils/package.json +++ b/utils/package.json @@ -1,7 +1,7 @@ { "name": "@onlook/utils", "description": "A shared utility library for Onlook", - "version": "0.0.2", + "version": "0.0.3", "type": "commonjs", "main": "dist/index.js", "module": "dist/index.mjs", From cd9164fc928a260a2d59e9eea48d6a7bfc84c10c Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 22 Sep 2024 14:18:40 -0400 Subject: [PATCH 38/40] Update utils package --- app/bun.lockb | Bin 426171 -> 364555 bytes app/package.json | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/bun.lockb b/app/bun.lockb index 4a8ce1db4c7151e70a7fd97114e3f728b0e9d008..44af83436c4f9755c391f2e20614f3de66f02274 100755 GIT binary patch delta 55270 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 J%AH;@`+vCpymSBn delta 94492 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 aGmY^;t2EIVW9Vu>MGHJ<$=r{vfBqlxGw>k* diff --git a/app/package.json b/app/package.json index e594b908b..676472bd6 100644 --- a/app/package.json +++ b/app/package.json @@ -36,7 +36,7 @@ "remove_tag": "./scripts/remove_tag.sh" }, "dependencies": { - "@onlook/utils": "^0.0.3", + "@onlook/utils": "0.0.3", "@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-context-menu": "^2.2.1", From 281efdd99e36bfe940381c992e84af2f8c0dae02 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 22 Sep 2024 14:44:33 -0400 Subject: [PATCH 39/40] Update random messages --- .../projects/ProjectsTab/Create/Load/Name.tsx | 3 +- .../projects/ProjectsTab/Create/New/Name.tsx | 23 +---- app/src/routes/projects/SettingsTab/index.tsx | 20 +---- app/src/routes/projects/helpers.ts | 35 ++++++++ cli/bun.lockb | Bin 34405 -> 34405 bytes cli/package.json | 80 +++++++++--------- 6 files changed, 81 insertions(+), 80 deletions(-) create mode 100644 app/src/routes/projects/helpers.ts diff --git a/app/src/routes/projects/ProjectsTab/Create/Load/Name.tsx b/app/src/routes/projects/ProjectsTab/Create/Load/Name.tsx index d6fe45ae5..33227fe3a 100644 --- a/app/src/routes/projects/ProjectsTab/Create/Load/Name.tsx +++ b/app/src/routes/projects/ProjectsTab/Create/Load/Name.tsx @@ -10,6 +10,7 @@ import { import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { StepProps } from '..'; +import { getRandomPlaceholder } from '../../../helpers'; export const LoadNameProject = ({ props: { projectData, currentStep, setProjectData, totalSteps, prevStep, nextStep }, @@ -35,7 +36,7 @@ export const LoadNameProject = ({ setProjectName(e.currentTarget.value)} /> diff --git a/app/src/routes/projects/ProjectsTab/Create/New/Name.tsx b/app/src/routes/projects/ProjectsTab/Create/New/Name.tsx index 4f9e1126c..b68c7b9bb 100644 --- a/app/src/routes/projects/ProjectsTab/Create/New/Name.tsx +++ b/app/src/routes/projects/ProjectsTab/Create/New/Name.tsx @@ -9,20 +9,14 @@ import { } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; -import { useEffect, useState } from 'react'; import { StepProps } from '..'; +import { getRandomPlaceholder } from '../../../helpers'; export const NewNameProject = ({ props: { projectData, currentStep, setProjectData, totalSteps, prevStep, nextStep }, }: { props: StepProps; }) => { - const [placeholderIndex, setPlaceholderIndex] = useState(0); - - useEffect(() => { - setPlaceholderIndex(Math.floor(Math.random() * PLACEHOLDERS.length)); - }, []); - function setProjectName(name: string) { setProjectData({ ...projectData, @@ -42,7 +36,7 @@ export const NewNameProject = ({ setProjectName(e.currentTarget.value)} /> @@ -67,16 +61,3 @@ export const NewNameProject = ({ ); }; - -const PLACEHOLDERS = [ - 'The greatest app in the world', - 'My epic project', - 'The greatest project ever', - 'A revolutionary idea', - 'Project X', - 'Genius React App', - 'The next billion dollar idea', - 'Mind-blowingly cool app', - 'Earth-shatteringly great app', - 'Moonshot project', -]; diff --git a/app/src/routes/projects/SettingsTab/index.tsx b/app/src/routes/projects/SettingsTab/index.tsx index 25224a109..7b6ec9aca 100644 --- a/app/src/routes/projects/SettingsTab/index.tsx +++ b/app/src/routes/projects/SettingsTab/index.tsx @@ -9,6 +9,7 @@ import { } from '@/components/ui/dropdown-menu'; import { CheckCircledIcon, ChevronDownIcon } from '@radix-ui/react-icons'; import { useEffect, useState } from 'react'; +import { getRandomSettingsMessage } from '../helpers'; import { MainChannels } from '/common/constants'; import { IDE, IdeType } from '/common/ide'; import { UserSettings } from '/common/models/settings'; @@ -17,21 +18,6 @@ export default function SettingsTab() { const [isAnalyticsEnabled, setIsAnalyticsEnabled] = useState(false); const [ide, setIde] = useState(IDE.VS_CODE); - const MESSAGES = [ - 'Set some dials and knobs and stuff', - 'Fine-tune how you want to build', - 'Swap out your default code editor if you dare', - "You shouldn't be worried about this stuff, yet here you are", - 'Mostly a formality', - "What's this button do?", - 'Customize how you want to build', - 'Thanks for stopping by the Settings page', - 'This is where the good stuff is', - 'Open 24 hours, 7 days a week', - '*beep boop*', - "Welcome. We've been expecting you.", - ]; - useEffect(() => { window.api.invoke(MainChannels.GET_USER_SETTINGS).then((res) => { const settings: UserSettings = res as UserSettings; @@ -40,8 +26,6 @@ export default function SettingsTab() { }); }, []); - const OPENING_MESSAGE = MESSAGES[Math.floor(Math.random() * MESSAGES.length)]; - function updateIde(ide: IDE) { window.api.invoke(MainChannels.UPDATE_USER_SETTINGS, { ideType: ide.type }); setIde(ide); @@ -56,7 +40,7 @@ export default function SettingsTab() {

{'Settings'}

-

{OPENING_MESSAGE}

+

{getRandomSettingsMessage()}

diff --git a/app/src/routes/projects/helpers.ts b/app/src/routes/projects/helpers.ts new file mode 100644 index 000000000..20daa4cb7 --- /dev/null +++ b/app/src/routes/projects/helpers.ts @@ -0,0 +1,35 @@ +export const PLACEHOLDER_NAMES = [ + 'The greatest app in the world', + 'My epic project', + 'The greatest project ever', + 'A revolutionary idea', + 'Project X', + 'Genius React App', + 'The next billion dollar idea', + 'Mind-blowingly cool app', + 'Earth-shatteringly great app', + 'Moonshot project', +]; + +export const SETTINGS_MESSAGE = [ + 'Set some dials and knobs and stuff', + 'Fine-tune how you want to build', + 'Swap out your default code editor if you dare', + "You shouldn't be worried about this stuff, yet here you are", + 'Mostly a formality', + "What's this button do?", + 'Customize how you want to build', + 'Thanks for stopping by the Settings page', + 'This is where the good stuff is', + 'Open 24 hours, 7 days a week', + '*beep boop*', + "Welcome. We've been expecting you.", +]; + +export function getRandomPlaceholder() { + return PLACEHOLDER_NAMES[Math.floor(Math.random() * PLACEHOLDER_NAMES.length)]; +} + +export function getRandomSettingsMessage() { + return SETTINGS_MESSAGE[Math.floor(Math.random() * SETTINGS_MESSAGE.length)]; +} diff --git a/cli/bun.lockb b/cli/bun.lockb index bf1d14419a016c9f00de5f03ff62b355a7b2e459..4c2b5409febc884e4c88ebbfc5d4b3713d2d607b 100755 GIT binary patch delta 144 zcmV;B0B`^0i~{A10+22saBQVoG4(?dfw9UTeiZ>>P7*)lTb>Zl_n`r7H@d^Hu}<0w zlVk`evrr3#9YB>SLvFHhcLjj^Ip03w;g0>?BIHy_NUrW=T%&K!4dn?el;O8kEY@(8 yXA7=0;h^NxN&$_&*^Qbyr9r@Mb(OOfDI7Hi0ssI200029I9}Wk0W-4|Z%`LL7(J8# delta 144 zcmV;B0B`^0i~{A10+22s4lha4)(2R?r7)$taGWe+B=&6&sIL`h_Jqv~qXB>In!%DuA{DI7Hi0bVdJFfK5&I9}Wk0Wh-_Z%`Knv^-P* diff --git a/cli/package.json b/cli/package.json index 4f519e8f7..a4638411f 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,42 +1,42 @@ { - "name": "onlook", - "description": "The Onlook Command Line Interface", - "version": "0.0.8", - "main": "dist/index.js", - "bin": { - "onlook": "dist/index.cjs" - }, - "directories": { - "test": "tests" - }, - "scripts": { - "esbuild": "esbuild src/index.ts --bundle --platform=node --format=cjs --outfile=dist/index.cjs", - "dev": "npm run esbuild -- --watch", - "build": "tsc --noEmit --skipLibCheck src/index.ts && esbuild src/index.ts --bundle --platform=node --format=cjs --outfile=dist/index.cjs --define:PACKAGE_VERSION=\\\"$npm_package_version\\\"", - "build:notype": "npm run esbuild", - "test": "bun test" - }, - "keywords": [ - "npx", - "onlook", - "setup", - "plugins" - ], - "author": { - "name": "Onlook", - "email": "contact@onlook.dev" - }, - "license": "Apache-2.0", - "homepage": "https://onlook.dev", - "devDependencies": { - "@types/bun": "latest", - "esbuild": "^0.23.1", - "tslib": "^2.6.3", - "typescript": "^5.0.0" - }, - "dependencies": { - "@onlook/utils": "^0.0.0", - "commander": "^12.1.0", - "ora": "^8.1.0" - } + "name": "onlook", + "description": "The Onlook Command Line Interface", + "version": "0.0.8", + "main": "dist/index.js", + "bin": { + "onlook": "dist/index.cjs" + }, + "directories": { + "test": "tests" + }, + "scripts": { + "esbuild": "esbuild src/index.ts --bundle --platform=node --format=cjs --outfile=dist/index.cjs", + "dev": "npm run esbuild -- --watch", + "build": "tsc --noEmit --skipLibCheck src/index.ts && esbuild src/index.ts --bundle --platform=node --format=cjs --outfile=dist/index.cjs --define:PACKAGE_VERSION=\\\"$npm_package_version\\\"", + "build:notype": "npm run esbuild", + "test": "bun test" + }, + "keywords": [ + "npx", + "onlook", + "setup", + "plugins" + ], + "author": { + "name": "Onlook", + "email": "contact@onlook.dev" + }, + "license": "Apache-2.0", + "homepage": "https://onlook.dev", + "devDependencies": { + "@types/bun": "latest", + "esbuild": "^0.23.1", + "tslib": "^2.6.3", + "typescript": "^5.0.0" + }, + "dependencies": { + "@onlook/utils": "^0.0.3", + "commander": "^12.1.0", + "ora": "^8.1.0" + } } \ No newline at end of file From 9ce57fab2b3d20b6217d506aff82131b8b235b23 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 22 Sep 2024 15:24:43 -0400 Subject: [PATCH 40/40] Clean up --- .../ProjectsTab/Create/Load/SelectFolder.tsx | 15 ++++++++++++--- .../projects/ProjectsTab/Create/Load/Verify.tsx | 14 ++++++++++---- .../projects/ProjectsTab/Create/New/Setup.tsx | 4 ++-- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/app/src/routes/projects/ProjectsTab/Create/Load/SelectFolder.tsx b/app/src/routes/projects/ProjectsTab/Create/Load/SelectFolder.tsx index c5d825c46..b77486022 100644 --- a/app/src/routes/projects/ProjectsTab/Create/Load/SelectFolder.tsx +++ b/app/src/routes/projects/ProjectsTab/Create/Load/SelectFolder.tsx @@ -40,6 +40,10 @@ export const LoadSelectFolder = ({ nextStep(); } + function handleClickPath() { + window.api.invoke(MainChannels.OPEN_IN_EXPLORER, projectData.folderPath); + } + return ( @@ -48,10 +52,15 @@ export const LoadSelectFolder = ({ {projectData.folderPath ? ( -
+

{projectData.name}

-

{projectData.folderPath}

+
{state === StepState.INSTALLED ? ( - + ) : ( - + )}
); diff --git a/app/src/routes/projects/ProjectsTab/Create/New/Setup.tsx b/app/src/routes/projects/ProjectsTab/Create/New/Setup.tsx index bace4bc7c..222039b82 100644 --- a/app/src/routes/projects/ProjectsTab/Create/New/Setup.tsx +++ b/app/src/routes/projects/ProjectsTab/Create/New/Setup.tsx @@ -71,13 +71,13 @@ export const NewSetupProject = ({

{projectData.name}

- +
)}