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