Skip to content

Commit

Permalink
fix(inspector) When loading component descriptors, take account of fi…
Browse files Browse the repository at this point in the history
…le mappings. (#5781)

- `importedFromWhere` now invokes `applyFilePathMappingsToFilePath`
  for the path that it returns so that it applies the path mapping rules to the
  results gained.
- Added a call to `getFilePathMappings` in a few places to support a call to
  `getComponentDescriptorForTarget`.
  • Loading branch information
seanparsons authored May 31, 2024
1 parent 425833f commit 872b256
Show file tree
Hide file tree
Showing 10 changed files with 183 additions and 5 deletions.
10 changes: 9 additions & 1 deletion editor/src/components/canvas/canvas-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import { isParseSuccess, isTextFile } from '../../core/shared/project-file-types
import {
applyUtopiaJSXComponentsChanges,
getDefaultExportedTopLevelElement,
getFilePathMappings,
getUtopiaJSXComponentsFromSuccess,
isRemixSceneElement,
} from '../../core/model/project-file-utils'
Expand Down Expand Up @@ -1700,8 +1701,15 @@ export function getValidElementPaths(
resolve: (importOrigin: string, toImport: string) => Either<string, string>,
getRemixValidPathsGenerationContext: (path: ElementPath) => RemixValidPathsGenerationContext,
): Array<ElementPath> {
const filePathMappings = getFilePathMappings(projectContents)
const { topLevelElements, imports } = getParseSuccessForFilePath(filePath, projectContents)
const importSource = importedFromWhere(filePath, topLevelElementName, topLevelElements, imports)
const importSource = importedFromWhere(
filePathMappings,
filePath,
topLevelElementName,
topLevelElements,
imports,
)
if (importSource != null) {
let originTopLevelName = getTopLevelName(importSource, topLevelElementName)
const resolvedImportSource = resolve(filePath, importSource.filePath)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import type { ComponentRendererComponent } from './component-renderer-component'
import { mapArrayToDictionary } from '../../../core/shared/array-utils'
import { assertNever } from '../../../core/shared/utils'
import { addFakeSpyEntry } from './ui-jsx-canvas-spy-wrapper'
import type { FilePathMappings } from '../../../core/model/project-file-utils'

function tryToGetInstancePath(
maybePath: ElementPath | null,
Expand Down Expand Up @@ -158,6 +159,7 @@ export function createComponentRendererComponent(params: {
highlightBounds: highlightBounds,
editedText: rerenderUtopiaContext.editedText,
variablesInScope: {},
filePathMappings: rerenderUtopiaContext.filePathMappings,
},
undefined,
codeError,
Expand Down Expand Up @@ -228,6 +230,7 @@ export function createComponentRendererComponent(params: {
code: code,
highlightBounds: highlightBounds,
editedText: rerenderUtopiaContext.editedText,
filePathMappings: rerenderUtopiaContext.filePathMappings,
}

const buildResult = React.useRef<React.ReactElement | null>(null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { left } from '../../../core/shared/either'
import type { ElementPath } from '../../../core/shared/project-file-types'
import type { ProjectContentTreeRoot } from '../../assets'
import type { TransientFilesState, UIFileBase64Blobs } from '../../editor/store/editor-state'
import type { FilePathMappings } from '../../../core/model/project-file-utils'

export interface MutableUtopiaCtxRefData {
[filePath: string]: {
Expand All @@ -32,6 +33,7 @@ interface RerenderUtopiaContextProps {
canvasIsLive: boolean
shouldIncludeCanvasRootInTheSpy: boolean
editedText: ElementPath | null
filePathMappings: FilePathMappings
}

export const RerenderUtopiaCtxAtom = atomWithPubSub<RerenderUtopiaContextProps>({
Expand All @@ -42,6 +44,7 @@ export const RerenderUtopiaCtxAtom = atomWithPubSub<RerenderUtopiaContextProps>(
canvasIsLive: false,
shouldIncludeCanvasRootInTheSpy: false,
editedText: null,
filePathMappings: [],
},
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import {
import { RemixSceneComponent } from './remix-scene-component'
import { STEGANOGRAPHY_ENABLED, isFeatureEnabled } from '../../../utils/feature-switches'
import { jsxElementChildToText } from './jsx-element-child-to-text'
import type { FilePathMappings } from '../../../core/model/project-file-utils'

export interface RenderContext {
rootScope: MapLike<any>
Expand All @@ -98,6 +99,7 @@ export interface RenderContext {
highlightBounds: HighlightBoundsForUids | null
editedText: ElementPath | null
variablesInScope: VariableData
filePathMappings: FilePathMappings
}

export function createLookupRender(
Expand Down Expand Up @@ -704,6 +706,7 @@ function renderJSXElement(
highlightBounds,
editedText,
variablesInScope,
filePathMappings,
} = renderContext
const createChildrenElement = (child: JSXElementChild): React.ReactChild => {
const childPath = optionalMap((path) => EP.appendToPath(path, getUtopiaID(child)), elementPath)
Expand All @@ -729,7 +732,7 @@ function renderJSXElement(
// elements from scope and import to confirm it's not a top level element.
const importedFrom = elementIsFragment
? null
: importedFromWhere(filePath, jsx.name.baseVariable, [], imports)
: importedFromWhere(filePathMappings, filePath, jsx.name.baseVariable, [], imports)

const isElementImportedFromModule = (moduleName: string, name: string) =>
!elementIsIntrinsic &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
applyBlockReturnFunctions,
} from '../../../core/shared/dom-utils'
import { emptySet } from '../../../core/shared/set-utils'
import { getFilePathMappings } from '../../../core/model/project-file-utils'

const emptyFileBlobs: UIFileBase64Blobs = {}

Expand Down Expand Up @@ -65,6 +66,7 @@ export function createExecutionScope(
topLevelJsxComponents: Map<string | null, UtopiaJSXComponent>
requireResult: MapLike<any>
} {
const filePathMappings = getFilePathMappings(projectContents)
if (!(filePath in topLevelComponentRendererComponents.current)) {
// we make sure that the ref has an entry for this filepath
topLevelComponentRendererComponents.current[filePath] = {}
Expand Down Expand Up @@ -148,6 +150,7 @@ export function createExecutionScope(
highlightBounds: highlightBounds,
editedText: editedText,
variablesInScope: {},
filePathMappings: filePathMappings,
},
null,
propertiesFromParams,
Expand Down
4 changes: 4 additions & 0 deletions editor/src/components/canvas/ui-jsx-canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ import { useAtom } from 'jotai'
import { RemixNavigationAtom } from './remix/utopia-remix-root-component'
import { IS_TEST_ENVIRONMENT } from '../../common/env-vars'
import { listenForReactRouterErrors } from '../../core/shared/runtime-report-logs'
import { getFilePathMappings } from '../../core/model/project-file-utils'

applyUIDMonkeyPatch()

Expand Down Expand Up @@ -495,12 +496,15 @@ export const UiJsxCanvas = React.memo<UiJsxCanvasPropsWithErrorCallback>((props)
validPaths: rootValidPathsSet,
})

const filePathMappings = getFilePathMappings(projectContents)

const rerenderUtopiaContextValue = useKeepShallowReferenceEquality({
hiddenInstances: hiddenInstances,
displayNoneInstances: displayNoneInstances,
canvasIsLive: canvasIsLive,
shouldIncludeCanvasRootInTheSpy: props.shouldIncludeCanvasRootInTheSpy,
editedText: props.editedText,
filePathMappings: filePathMappings,
})

const utopiaProjectContextValue = useKeepShallowReferenceEquality({
Expand Down
15 changes: 12 additions & 3 deletions editor/src/components/editor/import-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ import { withUnderlyingTarget } from './store/editor-state'
import * as EP from '../../core/shared/element-path'
import { renameDuplicateImportsInMergeResolution } from '../../core/shared/import-shared-utils'
import { getParseSuccessForFilePath } from '../canvas/canvas-utils'
import type { FilePathMappings } from '../../core/model/project-file-utils'
import {
applyFilePathMappingsToFilePath,
getFilePathMappings,
} from '../../core/model/project-file-utils'

export function getRequiredImportsForElement(
target: ElementPath,
Expand All @@ -44,6 +49,7 @@ export function getRequiredImportsForElement(
targetFilePath: string,
builtInDependencies: BuiltInDependencies,
): ImportsMergeResolution {
const filePathMappings = getFilePathMappings(projectContents)
return withUnderlyingTarget<ImportsMergeResolution>(
target,
projectContents,
Expand All @@ -62,6 +68,7 @@ export function getRequiredImportsForElement(
// Straight up ignore intrinsic elements as they wont be imported.
if (!isIntrinsicElement(elem.name)) {
const importedFromResult = importedFromWhere(
filePathMappings,
underlyingFilePath,
elem.name.baseVariable,
topLevelElementsInOriginFile,
Expand Down Expand Up @@ -144,6 +151,7 @@ export function getRequiredImportsForElement(
type ImportedFromWhereResult = ImportInfo

export function importedFromWhere(
filePathMappings: FilePathMappings,
originFilePath: string,
variableName: string,
topLevelElements: Array<TopLevelElement>,
Expand Down Expand Up @@ -172,15 +180,16 @@ export function importedFromWhere(
}
for (const importSource of Object.keys(importsToSearch)) {
const specificImport = importsToSearch[importSource]
const importSourceMapped = applyFilePathMappingsToFilePath(importSource, filePathMappings)
if (specificImport.importedAs === variableName) {
return importedOrigin(importSource, variableName, null)
return importedOrigin(importSourceMapped, variableName, null)
}
if (specificImport.importedWithName === variableName) {
return importedOrigin(importSource, variableName, null)
return importedOrigin(importSourceMapped, variableName, null)
}
for (const fromWithin of specificImport.importedFromWithin) {
if (fromWithin.alias === variableName) {
return importedOrigin(importSource, variableName, fromWithin.name)
return importedOrigin(importSourceMapped, variableName, fromWithin.name)
}
}
}
Expand Down
139 changes: 139 additions & 0 deletions editor/src/core/property-controls/property-controls-utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import type { ProjectContentTreeRoot } from '../../components/assets'
import { addFileToProjectContents } from '../../components/assets'
import type {
ComponentDescriptor,
PropertyControlsInfo,
} from '../../components/custom-code/code-file'
import { componentDescriptorFromDescriptorFile } from '../../components/custom-code/code-file'
import { simpleDefaultProject } from '../../sample-projects/sample-project-utils'
import { parseProjectContents } from '../../sample-projects/sample-project-utils.test-utils'
import { codeFile } from '../shared/project-file-types'
import { assertNever } from '../shared/utils'
import { getComponentDescriptorForTarget } from './property-controls-utils'
import * as EP from '../shared/element-path'

describe('getComponentDescriptorForTarget', () => {
function getProjectContents(mappedPath: 'mapped-path' | 'regular-path'): ProjectContentTreeRoot {
let baseProjectContents: ProjectContentTreeRoot = simpleDefaultProject().projectContents
baseProjectContents = addFileToProjectContents(
baseProjectContents,
'/jsconfig.json',
codeFile(
`{
"compilerOptions": {
"checkJs": true,
"jsx": "react-jsx",
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "Bundler",
"baseUrl": ".",
"paths": {
"~/*": ["app/*"]
}
},
"include": ["./**/*.d.ts", "./**/*.js", "./**/*.jsx"]
}
`,
null,
),
)
baseProjectContents = addFileToProjectContents(
baseProjectContents,
'/app/components.js',
codeFile(
`
import * as React from 'react'
export const Component = () => {
return <div data-uid='component-div' />
}
`,
null,
),
)
switch (mappedPath) {
case 'mapped-path':
baseProjectContents = addFileToProjectContents(
baseProjectContents,
'/src/app.js',
codeFile(
`
import * as React from 'react'
import { Component } from '~/components'
export const App = (props) => {
return (
<div
data-uid='div'
style={{ width: '100%', height: '100%', backgroundcolor: '#ffffff', position: 'relative' }}
>
<Component data-uid='component' />
</div>
)
}`,
null,
),
)
break
case 'regular-path':
baseProjectContents = addFileToProjectContents(
baseProjectContents,
'/src/app.js',
codeFile(
`
import * as react from 'react'
import { Component } from '/app/components'
export const App = (props) => {
return (
<div
data-uid='div'
style={{ width: '100%', height: '100%', backgroundcolor: '#ffffff', position: 'relative' }}
>
<Component data-uid='component' />
</div>
)
}`,
null,
),
)
break
default:
assertNever(mappedPath)
}

return parseProjectContents(baseProjectContents)
}
const componentDescriptor: ComponentDescriptor = {
properties: {},
supportsChildren: true,
preferredChildComponents: [],
variants: [],
source: componentDescriptorFromDescriptorFile('/components.utopia.js'),
focus: 'default',
inspector: { type: 'hidden' },
emphasis: 'regular',
icon: 'component',
label: 'Component',
}
const propertyControlsInfo: PropertyControlsInfo = {
['/app/components']: {
['Component']: componentDescriptor,
},
}
it('works with the regular path', () => {
const projectContents = getProjectContents('regular-path')
const actualResult = getComponentDescriptorForTarget(
EP.fromString(`sample-storyboard/sample-scene/sample-app:div/component`),
propertyControlsInfo,
projectContents,
)
expect(actualResult).toEqual(componentDescriptor)
})
it('works with a mapped path', () => {
const projectContents = getProjectContents('mapped-path')
const actualResult = getComponentDescriptorForTarget(
EP.fromString(`sample-storyboard/sample-scene/sample-app:div/component`),
propertyControlsInfo,
projectContents,
)
expect(actualResult).toEqual(componentDescriptor)
})
})
5 changes: 5 additions & 0 deletions editor/src/core/property-controls/property-controls-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { dropFileExtension } from '../shared/file-utils'
import type { Styling } from 'utopia-api'
import { StylingOptions } from 'utopia-api'
import { intersection } from '../shared/set-utils'
import { getFilePathMappings } from '../model/project-file-utils'

export function propertyControlsForComponentInFile(
componentName: string,
Expand All @@ -49,6 +50,7 @@ export function getPropertyControlsForTarget(
propertyControlsInfo: PropertyControlsInfo,
projectContents: ProjectContentTreeRoot,
): PropertyControls | null {
const filePathMappings = getFilePathMappings(projectContents)
return withUnderlyingTarget(
target,
projectContents,
Expand All @@ -61,6 +63,7 @@ export function getPropertyControlsForTarget(
) => {
if (isJSXElement(element)) {
const importedFrom = importedFromWhere(
filePathMappings,
underlyingFilePath,
element.name.baseVariable,
success.topLevelElements,
Expand Down Expand Up @@ -137,6 +140,7 @@ export function getComponentDescriptorForTarget(
propertyControlsInfo: PropertyControlsInfo,
projectContents: ProjectContentTreeRoot,
): ComponentDescriptor | null {
const filePathMappings = getFilePathMappings(projectContents)
return withUnderlyingTarget(
target,
projectContents,
Expand All @@ -149,6 +153,7 @@ export function getComponentDescriptorForTarget(
) => {
if (isJSXElement(element)) {
const importedFrom = importedFromWhere(
filePathMappings,
underlyingFilePath,
element.name.baseVariable,
success.topLevelElements,
Expand Down
1 change: 1 addition & 0 deletions editor/src/utils/utils.test-utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export const testRenderContext: RenderContext = {
highlightBounds: null,
editedText: null,
variablesInScope: {},
filePathMappings: [],
}

export function delay(time: number): Promise<void> {
Expand Down

0 comments on commit 872b256

Please sign in to comment.