From b6854bb0b144e959a67b916d7b25716aad230060 Mon Sep 17 00:00:00 2001 From: Liad Yosef Date: Tue, 29 Oct 2024 15:00:51 +0100 Subject: [PATCH] refactor(editor): separate requirements check --- .../src/components/editor/actions/actions.tsx | 162 +--------------- .../shared/github/operations/load-branch.ts | 12 +- .../check-utopia-requirements.ts | 177 +++++------------- .../requirements/requirement-language.ts | 48 +++++ .../requirements/requirement-package-json.ts | 54 ++++++ .../requirements/requirement-react.ts | 44 +++++ .../requirements/requirement-storyboard.ts | 157 ++++++++++++++++ .../utopia-requirements-types.ts | 15 ++ 8 files changed, 370 insertions(+), 299 deletions(-) create mode 100644 editor/src/core/shared/import/proejct-health-check/requirements/requirement-language.ts create mode 100644 editor/src/core/shared/import/proejct-health-check/requirements/requirement-package-json.ts create mode 100644 editor/src/core/shared/import/proejct-health-check/requirements/requirement-react.ts create mode 100644 editor/src/core/shared/import/proejct-health-check/requirements/requirement-storyboard.ts diff --git a/editor/src/components/editor/actions/actions.tsx b/editor/src/components/editor/actions/actions.tsx index 090d109b6898..c9ed2f8801ff 100644 --- a/editor/src/components/editor/actions/actions.tsx +++ b/editor/src/components/editor/actions/actions.tsx @@ -398,7 +398,6 @@ import { trueUpChildrenOfGroupChanged, trueUpHuggingElement, trueUpGroupElementChanged, - getPackageJsonFromProjectContents, modifyUnderlyingTargetJSXElement, getAllComponentDescriptorErrors, updatePackageJsonInEditorState, @@ -441,7 +440,6 @@ import { reorderElement } from '../../../components/canvas/commands/reorder-elem import type { BuiltInDependencies } from '../../../core/es-modules/package-manager/built-in-dependencies-list' import { fetchNodeModules } from '../../../core/es-modules/package-manager/fetch-packages' import { resolveModule } from '../../../core/es-modules/package-manager/module-resolution' -import { addStoryboardFileToProject } from '../../../core/model/storyboard-utils' import { UTOPIA_UID_KEY } from '../../../core/model/utopia-constants' import { mapDropNulls, uniqBy } from '../../../core/shared/array-utils' import type { TreeConflicts } from '../../../core/shared/github/helpers' @@ -630,13 +628,9 @@ import { getNavigatorTargetsFromEditorState } from '../../navigator/navigator-ut import { getParseCacheOptions } from '../../../core/shared/parse-cache-utils' import { styleP } from '../../inspector/inspector-common' import { getUpdateOperationResult } from '../../../core/shared/import/import-operation-service' -import { - notifyCheckingRequirement, - notifyResolveRequirement, - updateRequirements, -} from '../../../core/shared/import/proejct-health-check/utopia-requirements-service' -import { RequirementResolutionResult } from '../../../core/shared/import/proejct-health-check/utopia-requirements-types' +import { updateRequirements } from '../../../core/shared/import/proejct-health-check/utopia-requirements-service' import { applyValuesAtPath, deleteValuesAtPath } from '../../canvas/commands/utils/property-utils' +import { createStoryboardFileIfNecessary } from '../../../core/shared/import/proejct-health-check/requirements/requirement-storyboard' export const MIN_CODE_PANE_REOPEN_WIDTH = 100 @@ -1595,91 +1589,6 @@ function updateCodeEditorVisibility(editor: EditorModel, codePaneVisible: boolea } } -function createStoryboardFileIfRemixProject( - projectContents: ProjectContentTreeRoot, -): ProjectContentTreeRoot | null { - const packageJsonContents = defaultEither( - null, - getPackageJsonFromProjectContents(projectContents), - ) - if (packageJsonContents == null) { - return null - } - const remixNotIncluded = packageJsonContents['dependencies']?.['@remix-run/react'] == null - if (remixNotIncluded) { - return null - } - - const updatedProjectContents = addFileToProjectContents( - projectContents, - StoryboardFilePath, - codeFile(DefaultStoryboardWithRemix, null, 1), - ) - return updatedProjectContents -} - -function createStoryboardFileIfMainComponentPresent( - projectContents: ProjectContentTreeRoot, -): ProjectContentTreeRoot | null { - return addStoryboardFileToProject(projectContents) -} - -function createStoryboardFileWithPlaceholderContents( - projectContents: ProjectContentTreeRoot, - createPlaceholder: 'create-placeholder' | 'skip-creating-placeholder', -): ProjectContentTreeRoot { - if (createPlaceholder === 'skip-creating-placeholder') { - return projectContents - } - const updatedProjectContents = addFileToProjectContents( - projectContents, - StoryboardFilePath, - codeFile(DefaultStoryboardContents, null, 1), - ) - return updatedProjectContents -} - -export function createStoryboardFileIfNecessary( - dispatch: EditorDispatch, - projectContents: ProjectContentTreeRoot, - createPlaceholder: 'create-placeholder' | 'skip-creating-placeholder', -): ProjectContentTreeRoot { - notifyCheckingRequirement(dispatch, 'storyboard', 'Checking for storyboard.js') - const storyboardFile = getProjectFileByFilePath(projectContents, StoryboardFilePath) - if (storyboardFile != null) { - notifyResolveRequirement( - dispatch, - 'storyboard', - RequirementResolutionResult.Found, - 'Storyboard.js found', - ) - return projectContents - } - - const result = - createStoryboardFileIfRemixProject(projectContents) ?? - createStoryboardFileIfMainComponentPresent(projectContents) ?? - createStoryboardFileWithPlaceholderContents(projectContents, createPlaceholder) - - if (result == projectContents) { - notifyResolveRequirement( - dispatch, - 'storyboard', - RequirementResolutionResult.Partial, - 'Storyboard.js skipped', - ) - } else { - notifyResolveRequirement( - dispatch, - 'storyboard', - RequirementResolutionResult.Fixed, - 'Storyboard.js created', - ) - } - - return result -} - // JS Editor Actions: export const UPDATE_FNS = { NEW: ( @@ -4049,14 +3958,12 @@ export const UPDATE_FNS = { } return { ...editor, - projectContents: createStoryboardFileIfNecessary( - dispatch, - workingProjectContents, - // If we are in the process of cloning a Github repository, do not create placeholder Storyboard + projectContents: + // If we are in the process of cloning a Github repository, do not create storyboard + // it will be created in the requirements check phase userState.githubState.gitRepoToLoad != null - ? 'skip-creating-placeholder' - : 'create-placeholder', - ), + ? workingProjectContents + : createStoryboardFileIfNecessary(workingProjectContents), canvas: { ...editor.canvas, canvasContentInvalidateCount: anyParsedUpdates @@ -6462,61 +6369,6 @@ function saveFileInProjectContents( } } -const DefaultStoryboardWithRemix = `import * as React from 'react' -import { Storyboard, RemixScene } from 'utopia-api' - -export var storyboard = ( - - - -) -` - -const DefaultStoryboardContents = `import * as React from 'react' -import { Scene, Storyboard } from 'utopia-api' - -export var storyboard = ( - - - - Open the insert menu or press the + button in the - toolbar to insert components - - - - ) -` - function addTextFile( editor: EditorState, parentPath: string, diff --git a/editor/src/core/shared/github/operations/load-branch.ts b/editor/src/core/shared/github/operations/load-branch.ts index 36a63a7d74fc..8ff8ef7c9feb 100644 --- a/editor/src/core/shared/github/operations/load-branch.ts +++ b/editor/src/core/shared/github/operations/load-branch.ts @@ -33,9 +33,7 @@ import { saveGithubAsset, } from '../helpers' import type { GithubOperationContext } from './github-operation-context' -import { createStoryboardFileIfNecessary } from '../../../../components/editor/actions/actions' import { getAllComponentDescriptorFilePaths } from '../../../property-controls/property-controls-local' -import type { ExistingAsset } from '../../../../components/editor/server' import { GithubOperations } from '.' import { assertNever } from '../../utils' import { updateProjectContentsWithParseResults } from '../../parser-projectcontents-utils' @@ -173,16 +171,8 @@ export const updateProjectWithBranchContent = notifyOperationFinished(dispatch, { type: 'parseFiles' }, ImportOperationResult.Success) resetRequirementsResolutions(dispatch) - const parsedProjectContentsInitial = createStoryboardFileIfNecessary( - dispatch, - parseResults, - 'create-placeholder', - ) - const parsedProjectContents = checkAndFixUtopiaRequirements( - dispatch, - parsedProjectContentsInitial, - ) + const parsedProjectContents = checkAndFixUtopiaRequirements(dispatch, parseResults) // Update the editor with everything so that if anything else fails past this point // there's no loss of data from the user's perspective. diff --git a/editor/src/core/shared/import/proejct-health-check/check-utopia-requirements.ts b/editor/src/core/shared/import/proejct-health-check/check-utopia-requirements.ts index ec78bdcb05e6..6a23551a9c17 100644 --- a/editor/src/core/shared/import/proejct-health-check/check-utopia-requirements.ts +++ b/editor/src/core/shared/import/proejct-health-check/check-utopia-requirements.ts @@ -1,154 +1,65 @@ -import { - addFileToProjectContents, - packageJsonFileFromProjectContents, -} from '../../../../components/assets' +import { getProjectFileByFilePath } from '../../../../components/assets' import type { ProjectContentTreeRoot } from 'utopia-shared/src/types' -import { codeFile, isTextFile, RevisionsState } from '../../project-file-types' -import { notifyCheckingRequirement, notifyResolveRequirement } from './utopia-requirements-service' -import { RequirementResolutionResult } from './utopia-requirements-types' -import { applyToAllUIJSFiles } from '../../../../core/model/project-file-utils' +import { isTextFile } from '../../project-file-types' import type { EditorDispatch } from '../../../../components/editor/action-types' +import CheckPackageJson from './requirements/requirement-package-json' +import CheckLanguage from './requirements/requirement-language' +import CheckReactVersion from './requirements/requirement-react' +import type { RequirementCheck } from './utopia-requirements-types' +import { notifyCheckingRequirement, notifyResolveRequirement } from './utopia-requirements-service' +import CheckStoryboard from './requirements/requirement-storyboard' + +const checks: RequirementCheck[] = [ + new CheckStoryboard(), + new CheckPackageJson(), + new CheckLanguage(), + new CheckReactVersion(), +] export function checkAndFixUtopiaRequirements( dispatch: EditorDispatch, parsedProjectContents: ProjectContentTreeRoot, ): ProjectContentTreeRoot { let projectContents = parsedProjectContents - // check and fix package.json - projectContents = checkAndFixPackageJson(dispatch, projectContents) - // check language - checkProjectLanguage(dispatch, projectContents) - // check react version - checkReactVersion(dispatch, projectContents) - return projectContents -} - -function getPackageJson( - projectContents: ProjectContentTreeRoot, -): { utopia?: Record; dependencies?: Record } | null { - const packageJson = packageJsonFileFromProjectContents(projectContents) - if (packageJson != null && isTextFile(packageJson)) { - return JSON.parse(packageJson.fileContents.code) - } - return null -} - -function checkAndFixPackageJson( - dispatch: EditorDispatch, - projectContents: ProjectContentTreeRoot, -): ProjectContentTreeRoot { - notifyCheckingRequirement(dispatch, 'packageJsonEntries', 'Checking package.json') - const parsedPackageJson = getPackageJson(projectContents) - if (parsedPackageJson == null) { - notifyResolveRequirement( - dispatch, - 'packageJsonEntries', - RequirementResolutionResult.Critical, - 'The file package.json was not found', - ) - return projectContents - } - if (parsedPackageJson.utopia == null) { - parsedPackageJson.utopia = { - 'main-ui': 'utopia/storyboard.js', - } - const result = addFileToProjectContents( - projectContents, - '/package.json', - codeFile( - JSON.stringify(parsedPackageJson, null, 2), - null, - 0, - RevisionsState.CodeAheadButPleaseTellVSCodeAboutIt, - ), - ) - notifyResolveRequirement( - dispatch, - 'packageJsonEntries', - RequirementResolutionResult.Fixed, - 'Fixed utopia entry in package.json', - ) - return result - } else { + // iterate over all checks, updating the project contents as we go + for (const check of checks) { + const checkName = check.getRequirementName() + notifyCheckingRequirement(dispatch, checkName, check.getStartText()) + const checkResult = check.check(projectContents) notifyResolveRequirement( dispatch, - 'packageJsonEntries', - RequirementResolutionResult.Found, - 'Valid package.json found', + checkName, + checkResult.resolution, + checkResult.resultText, + checkResult.resultValue, ) + projectContents = checkResult.newProjectContents ?? projectContents } - return projectContents } -function checkProjectLanguage( - dispatch: EditorDispatch, +export function getPackageJson( projectContents: ProjectContentTreeRoot, -): void { - notifyCheckingRequirement(dispatch, 'language', 'Checking project language') - let jsCount = 0 - let tsCount = 0 - applyToAllUIJSFiles(projectContents, (filename, uiJSFile) => { - if ((filename.endsWith('.ts') || filename.endsWith('.tsx')) && !filename.endsWith('.d.ts')) { - tsCount++ - } else if (filename.endsWith('.js') || filename.endsWith('.jsx')) { - jsCount++ - } - return uiJSFile - }) - if (tsCount > 0) { - notifyResolveRequirement( - dispatch, - 'language', - RequirementResolutionResult.Critical, - 'There are Typescript files in the project', - 'typescript', - ) - } else if (jsCount == 0) { - // in case it's a .coffee project, python, etc - notifyResolveRequirement( - dispatch, - 'language', - RequirementResolutionResult.Critical, - 'No JS/JSX files found', - 'javascript', - ) - } else { - notifyResolveRequirement( - dispatch, - 'language', - RequirementResolutionResult.Found, - 'Project uses JS/JSX', - 'javascript', - ) - } +): { utopia?: Record; dependencies?: Record } | null { + return getJsonFile<{ utopia?: Record; dependencies?: Record }>( + projectContents, + '/package.json', + ) } -function checkReactVersion( - dispatch: EditorDispatch, +export function getPackageLockJson( projectContents: ProjectContentTreeRoot, -): void { - notifyCheckingRequirement(dispatch, 'reactVersion', 'Checking React version') - const parsedPackageJson = getPackageJson(projectContents) - if ( - parsedPackageJson == null || - parsedPackageJson.dependencies == null || - parsedPackageJson.dependencies.react == null - ) { - return notifyResolveRequirement( - dispatch, - 'reactVersion', - RequirementResolutionResult.Critical, - 'React is not in dependencies', - ) - } - const reactVersion = parsedPackageJson.dependencies.react - // TODO: check react version - return notifyResolveRequirement( - dispatch, - 'reactVersion', - RequirementResolutionResult.Found, - 'React version is ok', - reactVersion, +): { dependencies?: Record } | null { + return getJsonFile<{ dependencies?: Record }>( + projectContents, + '/package-lock.json', ) } + +function getJsonFile(projectContents: ProjectContentTreeRoot, fileName: string): T | null { + const file = getProjectFileByFilePath(projectContents, fileName) + if (file != null && isTextFile(file)) { + return JSON.parse(file.fileContents.code) as T + } + return null +} diff --git a/editor/src/core/shared/import/proejct-health-check/requirements/requirement-language.ts b/editor/src/core/shared/import/proejct-health-check/requirements/requirement-language.ts new file mode 100644 index 000000000000..f5d87ed5c257 --- /dev/null +++ b/editor/src/core/shared/import/proejct-health-check/requirements/requirement-language.ts @@ -0,0 +1,48 @@ +import type { ProjectContentTreeRoot } from 'utopia-shared/src/types' +import type { ProjectRequirements } from '../utopia-requirements-types' +import { + RequirementResolutionResult, + type RequirementCheck, + type RequirementCheckResult, +} from '../utopia-requirements-types' +import { applyToAllUIJSFiles } from '../../../../model/project-file-utils' + +export default class CheckProjectLanguage implements RequirementCheck { + getRequirementName(): keyof ProjectRequirements { + return 'language' + } + getStartText(): string { + return 'Checking project language' + } + check(projectContents: ProjectContentTreeRoot): RequirementCheckResult { + let jsCount = 0 + let tsCount = 0 + applyToAllUIJSFiles(projectContents, (filename, uiJSFile) => { + if ((filename.endsWith('.ts') || filename.endsWith('.tsx')) && !filename.endsWith('.d.ts')) { + tsCount++ + } else if (filename.endsWith('.js') || filename.endsWith('.jsx')) { + jsCount++ + } + return uiJSFile + }) + if (tsCount > 0) { + return { + resolution: RequirementResolutionResult.Critical, + resultText: 'There are Typescript files in the project', + resultValue: 'typescript', + } + } else if (jsCount == 0) { + // in case it's a .coffee project, python, etc + return { + resolution: RequirementResolutionResult.Critical, + resultText: 'No JS/JSX files found', + } + } else { + return { + resolution: RequirementResolutionResult.Found, + resultText: 'Project uses JS/JSX', + resultValue: 'javascript', + } + } + } +} diff --git a/editor/src/core/shared/import/proejct-health-check/requirements/requirement-package-json.ts b/editor/src/core/shared/import/proejct-health-check/requirements/requirement-package-json.ts new file mode 100644 index 000000000000..be2a8cdaa620 --- /dev/null +++ b/editor/src/core/shared/import/proejct-health-check/requirements/requirement-package-json.ts @@ -0,0 +1,54 @@ +import type { ProjectContentTreeRoot } from 'utopia-shared/src/types' +import { RevisionsState } from 'utopia-shared/src/types' +import { getPackageJson } from '../check-utopia-requirements' +import type { + ProjectRequirements, + RequirementCheck, + RequirementCheckResult, +} from '../utopia-requirements-types' +import { RequirementResolutionResult } from '../utopia-requirements-types' +import { addFileToProjectContents } from '../../../../../components/assets' +import { codeFile } from '../../../../../core/shared/project-file-types' + +export default class PackageJsonCheckAndFix implements RequirementCheck { + getRequirementName(): keyof ProjectRequirements { + return 'packageJsonEntries' + } + getStartText(): string { + return 'Checking package.json' + } + check(projectContents: ProjectContentTreeRoot): RequirementCheckResult { + const parsedPackageJson = getPackageJson(projectContents) + if (parsedPackageJson == null) { + return { + resolution: RequirementResolutionResult.Critical, + resultText: 'The file package.json was not found', + } + } + if (parsedPackageJson.utopia == null) { + parsedPackageJson.utopia = { + 'main-ui': 'utopia/storyboard.js', + } + const result = addFileToProjectContents( + projectContents, + '/package.json', + codeFile( + JSON.stringify(parsedPackageJson, null, 2), + null, + 0, + RevisionsState.CodeAheadButPleaseTellVSCodeAboutIt, + ), + ) + return { + resolution: RequirementResolutionResult.Fixed, + resultText: 'Added utopia entry to package.json', + newProjectContents: result, + } + } else { + return { + resolution: RequirementResolutionResult.Found, + resultText: 'Valid package.json found', + } + } + } +} diff --git a/editor/src/core/shared/import/proejct-health-check/requirements/requirement-react.ts b/editor/src/core/shared/import/proejct-health-check/requirements/requirement-react.ts new file mode 100644 index 000000000000..fd55df6a13a0 --- /dev/null +++ b/editor/src/core/shared/import/proejct-health-check/requirements/requirement-react.ts @@ -0,0 +1,44 @@ +import type { ProjectContentTreeRoot } from 'utopia-shared/src/types' +import type { ProjectRequirements } from '../utopia-requirements-types' +import { + RequirementResolutionResult, + type RequirementCheck, + type RequirementCheckResult, +} from '../utopia-requirements-types' +import { getPackageJson, getPackageLockJson } from '../check-utopia-requirements' +import Semver from 'semver' + +const SUPPORTED_REACT_VERSION_RANGE = '16.8.0 - 18.x' + +export default class CheckReactRequirement implements RequirementCheck { + getRequirementName(): keyof ProjectRequirements { + return 'reactVersion' + } + getStartText(): string { + return 'Checking React version' + } + check(projectContents: ProjectContentTreeRoot): RequirementCheckResult { + const parsedPackageLockJson = getPackageLockJson(projectContents) + // check package-lock.json first + let reactVersion = parsedPackageLockJson?.dependencies?.react + if (reactVersion == null) { + const parsedPackageJson = getPackageJson(projectContents) + // then check package.json + reactVersion = parsedPackageJson?.dependencies?.react + } + if (reactVersion == null) { + return { + resolution: RequirementResolutionResult.Critical, + resultText: 'React is not in dependencies', + } + } + const isMatching = Semver.intersects(reactVersion, SUPPORTED_REACT_VERSION_RANGE) + return { + resolution: isMatching + ? RequirementResolutionResult.Found + : RequirementResolutionResult.Critical, + resultText: isMatching ? 'React version is ok' : 'React version is not in supported range', + resultValue: reactVersion, + } + } +} diff --git a/editor/src/core/shared/import/proejct-health-check/requirements/requirement-storyboard.ts b/editor/src/core/shared/import/proejct-health-check/requirements/requirement-storyboard.ts new file mode 100644 index 000000000000..48dc95278068 --- /dev/null +++ b/editor/src/core/shared/import/proejct-health-check/requirements/requirement-storyboard.ts @@ -0,0 +1,157 @@ +import type { ProjectContentTreeRoot } from 'utopia-shared/src/types' +import type { ProjectRequirements, RequirementCheckResult } from '../utopia-requirements-types' +import { RequirementResolutionResult } from '../utopia-requirements-types' +import type { RequirementCheck } from '../utopia-requirements-types' +import { defaultEither } from '../../../../../core/shared/either' +import { StoryboardFilePath } from '../../../../../components/editor/store/editor-state' +import { getPackageJsonFromProjectContents } from '../../../../../components/editor/store/editor-state' +import { + addFileToProjectContents, + getProjectFileByFilePath, +} from '../../../../../components/assets' +import { codeFile } from '../../../../../core/shared/project-file-types' +import { addStoryboardFileToProject } from '../../../../../core/model/storyboard-utils' + +export default class CheckStoryboard implements RequirementCheck { + getRequirementName(): keyof ProjectRequirements { + return 'storyboard' + } + getStartText(): string { + return 'Checking for storyboard.js' + } + check(projectContents: ProjectContentTreeRoot): RequirementCheckResult { + return createStoryboardFileIfNecessaryInner(projectContents) + } +} + +export function createStoryboardFileIfNecessary( + projectContents: ProjectContentTreeRoot, +): ProjectContentTreeRoot { + const result = createStoryboardFileIfNecessaryInner(projectContents) + return result.newProjectContents ?? projectContents +} + +function createStoryboardFileIfNecessaryInner( + projectContents: ProjectContentTreeRoot, +): RequirementCheckResult { + const storyboardFile = getProjectFileByFilePath(projectContents, StoryboardFilePath) + if (storyboardFile != null) { + return { + resolution: RequirementResolutionResult.Found, + resultText: 'Storyboard.js found', + } + } + + const result = + createStoryboardFileIfRemixProject(projectContents) ?? + createStoryboardFileIfMainComponentPresent(projectContents) ?? + createStoryboardFileWithPlaceholderContents(projectContents) + + if (result == projectContents) { + return { + resolution: RequirementResolutionResult.Partial, + resultText: 'Storyboard.js skipped', + } + } else { + return { + resolution: RequirementResolutionResult.Fixed, + resultText: 'Storyboard.js created', + newProjectContents: result, + } + } +} + +function createStoryboardFileIfRemixProject( + projectContents: ProjectContentTreeRoot, +): ProjectContentTreeRoot | null { + const packageJsonContents = defaultEither( + null, + getPackageJsonFromProjectContents(projectContents), + ) + if (packageJsonContents == null) { + return null + } + const remixNotIncluded = packageJsonContents['dependencies']?.['@remix-run/react'] == null + if (remixNotIncluded) { + return null + } + + const updatedProjectContents = addFileToProjectContents( + projectContents, + StoryboardFilePath, + codeFile(DefaultStoryboardWithRemix, null, 1), + ) + return updatedProjectContents +} + +function createStoryboardFileIfMainComponentPresent( + projectContents: ProjectContentTreeRoot, +): ProjectContentTreeRoot | null { + return addStoryboardFileToProject(projectContents) +} + +function createStoryboardFileWithPlaceholderContents( + projectContents: ProjectContentTreeRoot, +): ProjectContentTreeRoot { + const updatedProjectContents = addFileToProjectContents( + projectContents, + StoryboardFilePath, + codeFile(DefaultStoryboardContents, null, 1), + ) + return updatedProjectContents +} + +const DefaultStoryboardWithRemix = `import * as React from 'react' +import { Storyboard, RemixScene } from 'utopia-api' + +export var storyboard = ( + + + +) +` + +const DefaultStoryboardContents = `import * as React from 'react' +import { Scene, Storyboard } from 'utopia-api' + +export var storyboard = ( + + + + Open the insert menu or press the + button in the + toolbar to insert components + + + + ) +` diff --git a/editor/src/core/shared/import/proejct-health-check/utopia-requirements-types.ts b/editor/src/core/shared/import/proejct-health-check/utopia-requirements-types.ts index 6af5548346f4..1b78ca09c69a 100644 --- a/editor/src/core/shared/import/proejct-health-check/utopia-requirements-types.ts +++ b/editor/src/core/shared/import/proejct-health-check/utopia-requirements-types.ts @@ -1,3 +1,5 @@ +import type { ProjectContentTreeRoot } from 'utopia-shared/src/types' + export const RequirementResolutionResult = { Found: 'found', Fixed: 'fixed', @@ -74,3 +76,16 @@ export function newProjectRequirements( reactVersion, } } + +export interface RequirementCheck { + check: (projectContents: ProjectContentTreeRoot) => { + resolution: RequirementResolutionResult + resultText: string + resultValue?: string + newProjectContents?: ProjectContentTreeRoot | null + } + getRequirementName: () => keyof ProjectRequirements + getStartText: () => string +} + +export type RequirementCheckResult = ReturnType