Fix deletion of unassigned standalone edge treatments (#6815)
* oops, make it nicer for no reason * tests * deleteTopLevelStatement * little swap * astMod edits * typos * add playwright test for chamfers * scene.settled instead of page.waitForTimeout * unfuck circular dep - move locateExtrudeDeclarator * locateExtrudeDeclarator > locateVariableWithCallOrPipe * fmt * edit the comment
This commit is contained in:
		@ -2388,6 +2388,7 @@ fillet001 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg01)])
 | 
			
		||||
    scene,
 | 
			
		||||
    editor,
 | 
			
		||||
    toolbar,
 | 
			
		||||
    cmdBar,
 | 
			
		||||
  }) => {
 | 
			
		||||
    // Code samples
 | 
			
		||||
    const initialCode = `sketch001 = startSketchOn(XY)
 | 
			
		||||
@ -2401,14 +2402,14 @@ extrude001 = extrude(sketch001, length = -12)
 | 
			
		||||
  |> fillet(radius = 5, tags = [seg01]) // fillet01
 | 
			
		||||
  |> fillet(radius = 5, tags = [seg02]) // fillet02
 | 
			
		||||
fillet03 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg01)])
 | 
			
		||||
fillet04 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])
 | 
			
		||||
fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])
 | 
			
		||||
`
 | 
			
		||||
    const pipedFilletDeclaration = 'fillet(radius = 5, tags = [seg01])'
 | 
			
		||||
    const firstPipedFilletDeclaration = 'fillet(radius = 5, tags = [seg01])'
 | 
			
		||||
    const secondPipedFilletDeclaration = 'fillet(radius = 5, tags = [seg02])'
 | 
			
		||||
    const standaloneFilletDeclaration =
 | 
			
		||||
    const standaloneAssignedFilletDeclaration =
 | 
			
		||||
      'fillet03 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg01)])'
 | 
			
		||||
    const secondStandaloneFilletDeclaration =
 | 
			
		||||
      'fillet04 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])'
 | 
			
		||||
    const standaloneUnassignedFilletDeclaration =
 | 
			
		||||
      'fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])'
 | 
			
		||||
 | 
			
		||||
    // Locators
 | 
			
		||||
    const pipedFilletEdgeLocation = { x: 600, y: 193 }
 | 
			
		||||
@ -2430,6 +2431,7 @@ fillet04 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])
 | 
			
		||||
      }, initialCode)
 | 
			
		||||
      await page.setBodyDimensions({ width: 1000, height: 500 })
 | 
			
		||||
      await homePage.goToModelingScene()
 | 
			
		||||
      await scene.settled(cmdBar)
 | 
			
		||||
 | 
			
		||||
      // verify modeling scene is loaded
 | 
			
		||||
      await scene.expectPixelColor(
 | 
			
		||||
@ -2446,15 +2448,19 @@ fillet04 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])
 | 
			
		||||
    await test.step('Delete fillet via feature tree selection', async () => {
 | 
			
		||||
      await test.step('Open Feature Tree Pane', async () => {
 | 
			
		||||
        await toolbar.openPane('feature-tree')
 | 
			
		||||
        await page.waitForTimeout(500)
 | 
			
		||||
        await scene.settled(cmdBar)
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      await test.step('Delete piped fillet via feature tree selection', async () => {
 | 
			
		||||
        await test.step('Verify all fillets are present in the editor', async () => {
 | 
			
		||||
          await editor.expectEditor.toContain(pipedFilletDeclaration)
 | 
			
		||||
          await editor.expectEditor.toContain(firstPipedFilletDeclaration)
 | 
			
		||||
          await editor.expectEditor.toContain(secondPipedFilletDeclaration)
 | 
			
		||||
          await editor.expectEditor.toContain(standaloneFilletDeclaration)
 | 
			
		||||
          await editor.expectEditor.toContain(secondStandaloneFilletDeclaration)
 | 
			
		||||
          await editor.expectEditor.toContain(
 | 
			
		||||
            standaloneAssignedFilletDeclaration
 | 
			
		||||
          )
 | 
			
		||||
          await editor.expectEditor.toContain(
 | 
			
		||||
            standaloneUnassignedFilletDeclaration
 | 
			
		||||
          )
 | 
			
		||||
        })
 | 
			
		||||
        await test.step('Verify test fillets are present in the scene', async () => {
 | 
			
		||||
          await scene.expectPixelColor(
 | 
			
		||||
@ -2475,13 +2481,17 @@ fillet04 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])
 | 
			
		||||
          )
 | 
			
		||||
          await operationButton.click({ button: 'left' })
 | 
			
		||||
          await page.keyboard.press('Delete')
 | 
			
		||||
          await page.waitForTimeout(500)
 | 
			
		||||
          await scene.settled(cmdBar)
 | 
			
		||||
        })
 | 
			
		||||
        await test.step('Verify piped fillet is deleted but other fillets are not (in the editor)', async () => {
 | 
			
		||||
          await editor.expectEditor.not.toContain(pipedFilletDeclaration)
 | 
			
		||||
          await editor.expectEditor.not.toContain(firstPipedFilletDeclaration)
 | 
			
		||||
          await editor.expectEditor.toContain(secondPipedFilletDeclaration)
 | 
			
		||||
          await editor.expectEditor.toContain(standaloneFilletDeclaration)
 | 
			
		||||
          await editor.expectEditor.toContain(secondStandaloneFilletDeclaration)
 | 
			
		||||
          await editor.expectEditor.toContain(
 | 
			
		||||
            standaloneAssignedFilletDeclaration
 | 
			
		||||
          )
 | 
			
		||||
          await editor.expectEditor.toContain(
 | 
			
		||||
            standaloneUnassignedFilletDeclaration
 | 
			
		||||
          )
 | 
			
		||||
        })
 | 
			
		||||
        await test.step('Verify piped fillet is deleted but non-piped is not (in the scene)', async () => {
 | 
			
		||||
          await scene.expectPixelColor(
 | 
			
		||||
@ -2497,22 +2507,51 @@ fillet04 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])
 | 
			
		||||
        })
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      await test.step('Delete non-piped fillet via feature tree selection', async () => {
 | 
			
		||||
        await test.step('Delete non-piped fillet', async () => {
 | 
			
		||||
      await test.step('Delete standalone assigned fillet via feature tree selection', async () => {
 | 
			
		||||
        await test.step('Delete standalone assigned fillet', async () => {
 | 
			
		||||
          const operationButton = await toolbar.getFeatureTreeOperation(
 | 
			
		||||
            'Fillet',
 | 
			
		||||
            1
 | 
			
		||||
          )
 | 
			
		||||
          await operationButton.click({ button: 'left' })
 | 
			
		||||
          await page.keyboard.press('Delete')
 | 
			
		||||
          await page.waitForTimeout(500)
 | 
			
		||||
          await scene.settled(cmdBar)
 | 
			
		||||
        })
 | 
			
		||||
        await test.step('Verify non-piped fillet is deleted but other two fillets are not (in the editor)', async () => {
 | 
			
		||||
        await test.step('Verify standalone assigned fillet is deleted but other two fillets are not (in the editor)', async () => {
 | 
			
		||||
          await editor.expectEditor.toContain(secondPipedFilletDeclaration)
 | 
			
		||||
          await editor.expectEditor.not.toContain(standaloneFilletDeclaration)
 | 
			
		||||
          await editor.expectEditor.toContain(secondStandaloneFilletDeclaration)
 | 
			
		||||
          await editor.expectEditor.not.toContain(
 | 
			
		||||
            standaloneAssignedFilletDeclaration
 | 
			
		||||
          )
 | 
			
		||||
          await editor.expectEditor.toContain(
 | 
			
		||||
            standaloneUnassignedFilletDeclaration
 | 
			
		||||
          )
 | 
			
		||||
        })
 | 
			
		||||
        await test.step('Verify non-piped fillet is deleted but piped is not (in the scene)', async () => {
 | 
			
		||||
        await test.step('Verify standalone assigned fillet is deleted but piped is not (in the scene)', async () => {
 | 
			
		||||
          await scene.expectPixelColor(
 | 
			
		||||
            edgeColorWhite,
 | 
			
		||||
            standaloneFilletEdgeLocation,
 | 
			
		||||
            lowTolerance
 | 
			
		||||
          )
 | 
			
		||||
        })
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      await test.step('Delete standalone unassigned fillet via feature tree selection', async () => {
 | 
			
		||||
        await test.step('Delete standalone unassigned fillet', async () => {
 | 
			
		||||
          const operationButton = await toolbar.getFeatureTreeOperation(
 | 
			
		||||
            'Fillet',
 | 
			
		||||
            1
 | 
			
		||||
          )
 | 
			
		||||
          await operationButton.click({ button: 'left' })
 | 
			
		||||
          await page.keyboard.press('Delete')
 | 
			
		||||
          await scene.settled(cmdBar)
 | 
			
		||||
        })
 | 
			
		||||
        await test.step('Verify standalone unassigned fillet is deleted but other fillet is not (in the editor)', async () => {
 | 
			
		||||
          await editor.expectEditor.toContain(secondPipedFilletDeclaration)
 | 
			
		||||
          await editor.expectEditor.not.toContain(
 | 
			
		||||
            standaloneUnassignedFilletDeclaration
 | 
			
		||||
          )
 | 
			
		||||
        })
 | 
			
		||||
        await test.step('Verify standalone unassigned fillet is deleted but piped is not (in the scene)', async () => {
 | 
			
		||||
          await scene.expectPixelColor(
 | 
			
		||||
            edgeColorWhite,
 | 
			
		||||
            standaloneFilletEdgeLocation,
 | 
			
		||||
@ -2964,14 +3003,14 @@ extrude001 = extrude(sketch001, length = -12)
 | 
			
		||||
  |> chamfer(length = 5, tags = [seg01]) // chamfer01
 | 
			
		||||
  |> chamfer(length = 5, tags = [seg02]) // chamfer02
 | 
			
		||||
chamfer03 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg01)])
 | 
			
		||||
chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])
 | 
			
		||||
chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])
 | 
			
		||||
`
 | 
			
		||||
    const pipedChamferDeclaration = 'chamfer(length = 5, tags = [seg01])'
 | 
			
		||||
    const firstPipedChamferDeclaration = 'chamfer(length = 5, tags = [seg01])'
 | 
			
		||||
    const secondPipedChamferDeclaration = 'chamfer(length = 5, tags = [seg02])'
 | 
			
		||||
    const standaloneChamferDeclaration =
 | 
			
		||||
    const standaloneAssignedChamferDeclaration =
 | 
			
		||||
      'chamfer03 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg01)])'
 | 
			
		||||
    const secondStandaloneChamferDeclaration =
 | 
			
		||||
      'chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])'
 | 
			
		||||
    const standaloneUnassignedChamferDeclaration =
 | 
			
		||||
      'chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])'
 | 
			
		||||
 | 
			
		||||
    // Locators
 | 
			
		||||
    const pipedChamferEdgeLocation = { x: 600, y: 193 }
 | 
			
		||||
@ -3010,16 +3049,18 @@ chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])
 | 
			
		||||
    await test.step('Delete chamfer via feature tree selection', async () => {
 | 
			
		||||
      await test.step('Open Feature Tree Pane', async () => {
 | 
			
		||||
        await toolbar.openPane('feature-tree')
 | 
			
		||||
        await page.waitForTimeout(500)
 | 
			
		||||
        await scene.settled(cmdBar)
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      await test.step('Delete piped chamfer via feature tree selection', async () => {
 | 
			
		||||
        await test.step('Verify all chamfers are present in the editor', async () => {
 | 
			
		||||
          await editor.expectEditor.toContain(pipedChamferDeclaration)
 | 
			
		||||
          await editor.expectEditor.toContain(firstPipedChamferDeclaration)
 | 
			
		||||
          await editor.expectEditor.toContain(secondPipedChamferDeclaration)
 | 
			
		||||
          await editor.expectEditor.toContain(standaloneChamferDeclaration)
 | 
			
		||||
          await editor.expectEditor.toContain(
 | 
			
		||||
            secondStandaloneChamferDeclaration
 | 
			
		||||
            standaloneAssignedChamferDeclaration
 | 
			
		||||
          )
 | 
			
		||||
          await editor.expectEditor.toContain(
 | 
			
		||||
            standaloneUnassignedChamferDeclaration
 | 
			
		||||
          )
 | 
			
		||||
        })
 | 
			
		||||
        await test.step('Verify test chamfers are present in the scene', async () => {
 | 
			
		||||
@ -3041,14 +3082,16 @@ chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])
 | 
			
		||||
          )
 | 
			
		||||
          await operationButton.click({ button: 'left' })
 | 
			
		||||
          await page.keyboard.press('Delete')
 | 
			
		||||
          await page.waitForTimeout(500)
 | 
			
		||||
          await scene.settled(cmdBar)
 | 
			
		||||
        })
 | 
			
		||||
        await test.step('Verify piped chamfer is deleted but other chamfers are not (in the editor)', async () => {
 | 
			
		||||
          await editor.expectEditor.not.toContain(pipedChamferDeclaration)
 | 
			
		||||
          await editor.expectEditor.not.toContain(firstPipedChamferDeclaration)
 | 
			
		||||
          await editor.expectEditor.toContain(secondPipedChamferDeclaration)
 | 
			
		||||
          await editor.expectEditor.toContain(standaloneChamferDeclaration)
 | 
			
		||||
          await editor.expectEditor.toContain(
 | 
			
		||||
            secondStandaloneChamferDeclaration
 | 
			
		||||
            standaloneAssignedChamferDeclaration
 | 
			
		||||
          )
 | 
			
		||||
          await editor.expectEditor.toContain(
 | 
			
		||||
            standaloneUnassignedChamferDeclaration
 | 
			
		||||
          )
 | 
			
		||||
        })
 | 
			
		||||
        await test.step('Verify piped chamfer is deleted but non-piped is not (in the scene)', async () => {
 | 
			
		||||
@ -3065,24 +3108,51 @@ chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])
 | 
			
		||||
        })
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      await test.step('Delete non-piped chamfer via feature tree selection', async () => {
 | 
			
		||||
        await test.step('Delete non-piped chamfer', async () => {
 | 
			
		||||
      await test.step('Delete standalone assigned chamfer via feature tree selection', async () => {
 | 
			
		||||
        await test.step('Delete standalone assigned chamfer', async () => {
 | 
			
		||||
          const operationButton = await toolbar.getFeatureTreeOperation(
 | 
			
		||||
            'Chamfer',
 | 
			
		||||
            1
 | 
			
		||||
          )
 | 
			
		||||
          await operationButton.click({ button: 'left' })
 | 
			
		||||
          await page.keyboard.press('Delete')
 | 
			
		||||
          await page.waitForTimeout(500)
 | 
			
		||||
          await scene.settled(cmdBar)
 | 
			
		||||
        })
 | 
			
		||||
        await test.step('Verify non-piped chamfer is deleted but other two chamfers are not (in the editor)', async () => {
 | 
			
		||||
        await test.step('Verify standalone assigned chamfer is deleted but other two chamfers are not (in the editor)', async () => {
 | 
			
		||||
          await editor.expectEditor.toContain(secondPipedChamferDeclaration)
 | 
			
		||||
          await editor.expectEditor.not.toContain(standaloneChamferDeclaration)
 | 
			
		||||
          await editor.expectEditor.not.toContain(
 | 
			
		||||
            standaloneAssignedChamferDeclaration
 | 
			
		||||
          )
 | 
			
		||||
          await editor.expectEditor.toContain(
 | 
			
		||||
            secondStandaloneChamferDeclaration
 | 
			
		||||
            standaloneUnassignedChamferDeclaration
 | 
			
		||||
          )
 | 
			
		||||
        })
 | 
			
		||||
        await test.step('Verify non-piped chamfer is deleted but piped is not (in the scene)', async () => {
 | 
			
		||||
        await test.step('Verify standalone assigned chamfer is deleted but piped is not (in the scene)', async () => {
 | 
			
		||||
          await scene.expectPixelColor(
 | 
			
		||||
            edgeColorWhite,
 | 
			
		||||
            standaloneChamferEdgeLocation,
 | 
			
		||||
            lowTolerance
 | 
			
		||||
          )
 | 
			
		||||
        })
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      await test.step('Delete standalone unassigned chamfer via feature tree selection', async () => {
 | 
			
		||||
        await test.step('Delete standalone unassigned chamfer', async () => {
 | 
			
		||||
          const operationButton = await toolbar.getFeatureTreeOperation(
 | 
			
		||||
            'Chamfer',
 | 
			
		||||
            1
 | 
			
		||||
          )
 | 
			
		||||
          await operationButton.click({ button: 'left' })
 | 
			
		||||
          await page.keyboard.press('Delete')
 | 
			
		||||
          await scene.settled(cmdBar)
 | 
			
		||||
        })
 | 
			
		||||
        await test.step('Verify standalone unassigned chamfer is deleted but piped chamfer is not (in the editor)', async () => {
 | 
			
		||||
          await editor.expectEditor.toContain(secondPipedChamferDeclaration)
 | 
			
		||||
          await editor.expectEditor.not.toContain(
 | 
			
		||||
            standaloneUnassignedChamferDeclaration
 | 
			
		||||
          )
 | 
			
		||||
        })
 | 
			
		||||
        await test.step('Verify standalone unassigned chamfer is deleted but piped is not (in the scene)', async () => {
 | 
			
		||||
          await scene.expectPixelColor(
 | 
			
		||||
            edgeColorWhite,
 | 
			
		||||
            standaloneChamferEdgeLocation,
 | 
			
		||||
 | 
			
		||||
@ -64,7 +64,7 @@ import { KCL_DEFAULT_CONSTANT_PREFIXES } from '@src/lib/constants'
 | 
			
		||||
import type { DefaultPlaneStr } from '@src/lib/planes'
 | 
			
		||||
 | 
			
		||||
import { err, trap } from '@src/lib/trap'
 | 
			
		||||
import { isOverlap, roundOff } from '@src/lib/utils'
 | 
			
		||||
import { isArray, isOverlap, roundOff } from '@src/lib/utils'
 | 
			
		||||
import type { ExtrudeFacePlane } from '@src/machines/modelingMachine'
 | 
			
		||||
import { ARG_AT } from '@src/lang/constants'
 | 
			
		||||
 | 
			
		||||
@ -949,6 +949,27 @@ export function deleteSegmentFromPipeExpression(
 | 
			
		||||
  return _modifiedAst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Deletes a standalone top level statement from the AST
 | 
			
		||||
 * Used for removing both unassigned statements and variable declarations
 | 
			
		||||
 *
 | 
			
		||||
 * @param ast The AST to modify
 | 
			
		||||
 * @param pathToNode The path to the node to delete
 | 
			
		||||
 */
 | 
			
		||||
export function deleteTopLevelStatement(
 | 
			
		||||
  ast: Node<Program>,
 | 
			
		||||
  pathToNode: PathToNode
 | 
			
		||||
): Error | void {
 | 
			
		||||
  const pathStep = pathToNode[1]
 | 
			
		||||
  if (!isArray(pathStep) || typeof pathStep[0] !== 'number') {
 | 
			
		||||
    return new Error(
 | 
			
		||||
      'Invalid pathToNode structure: expected a number at path[1][0]'
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
  const statementIndex: number = pathStep[0]
 | 
			
		||||
  ast.body.splice(statementIndex, 1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function removeSingleConstraintInfo(
 | 
			
		||||
  pathToCallExp: PathToNode,
 | 
			
		||||
  argDetails: SimplifiedArgDetails,
 | 
			
		||||
 | 
			
		||||
@ -801,7 +801,7 @@ extrude001 = extrude(sketch001, length = -15)`
 | 
			
		||||
          expectedCode
 | 
			
		||||
        )
 | 
			
		||||
      }, 10_000)
 | 
			
		||||
      it(`should delete a non-piped ${edgeTreatmentType} from a single segment`, async () => {
 | 
			
		||||
      it(`should delete a standalone assigned ${edgeTreatmentType} from a single segment`, async () => {
 | 
			
		||||
        const code = `sketch001 = startSketchOn(XY)
 | 
			
		||||
  |> startProfile(at = [-10, 10])
 | 
			
		||||
  |> line(end = [20, 0])
 | 
			
		||||
@ -810,8 +810,34 @@ extrude001 = extrude(sketch001, length = -15)`
 | 
			
		||||
  |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | 
			
		||||
  |> close()
 | 
			
		||||
extrude001 = extrude(sketch001, length = -15)
 | 
			
		||||
fillet001 = ${edgeTreatmentType}(extrude001, ${parameterName} = 3, tags = [seg01])`
 | 
			
		||||
        const edgeTreatmentSnippet = `fillet001 = ${edgeTreatmentType}(extrude001, ${parameterName} = 3, tags = [seg01])`
 | 
			
		||||
${edgeTreatmentType}001 = ${edgeTreatmentType}(extrude001, ${parameterName} = 3, tags = [seg01])`
 | 
			
		||||
        const edgeTreatmentSnippet = `${edgeTreatmentType}001 = ${edgeTreatmentType}(extrude001, ${parameterName} = 3, tags = [seg01])`
 | 
			
		||||
        const expectedCode = `sketch001 = startSketchOn(XY)
 | 
			
		||||
  |> startProfile(at = [-10, 10])
 | 
			
		||||
  |> line(end = [20, 0])
 | 
			
		||||
  |> line(end = [0, -20])
 | 
			
		||||
  |> line(end = [-20, 0], tag = $seg01)
 | 
			
		||||
  |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | 
			
		||||
  |> close()
 | 
			
		||||
extrude001 = extrude(sketch001, length = -15)`
 | 
			
		||||
 | 
			
		||||
        await runDeleteEdgeTreatmentTest(
 | 
			
		||||
          code,
 | 
			
		||||
          edgeTreatmentSnippet,
 | 
			
		||||
          expectedCode
 | 
			
		||||
        )
 | 
			
		||||
      }, 10_000)
 | 
			
		||||
      it(`should delete a standalone ${edgeTreatmentType} without assignment from a single segment`, async () => {
 | 
			
		||||
        const code = `sketch001 = startSketchOn(XY)
 | 
			
		||||
  |> startProfile(at = [-10, 10])
 | 
			
		||||
  |> line(end = [20, 0])
 | 
			
		||||
  |> line(end = [0, -20])
 | 
			
		||||
  |> line(end = [-20, 0], tag = $seg01)
 | 
			
		||||
  |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | 
			
		||||
  |> close()
 | 
			
		||||
extrude001 = extrude(sketch001, length = -15)
 | 
			
		||||
${edgeTreatmentType}(extrude001, ${parameterName} = 5, tags = [seg01])`
 | 
			
		||||
        const edgeTreatmentSnippet = `${edgeTreatmentType}(extrude001, ${parameterName} = 5, tags = [seg01])`
 | 
			
		||||
        const expectedCode = `sketch001 = startSketchOn(XY)
 | 
			
		||||
  |> startProfile(at = [-10, 10])
 | 
			
		||||
  |> line(end = [20, 0])
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,7 @@ import {
 | 
			
		||||
  getNodeFromPath,
 | 
			
		||||
  hasSketchPipeBeenExtruded,
 | 
			
		||||
  traverse,
 | 
			
		||||
  locateVariableWithCallOrPipe,
 | 
			
		||||
} from '@src/lang/queryAst'
 | 
			
		||||
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
 | 
			
		||||
import type { Artifact } from '@src/lang/std/artifactGraph'
 | 
			
		||||
@ -26,25 +27,25 @@ import {
 | 
			
		||||
  sketchLineHelperMapKw,
 | 
			
		||||
} from '@src/lang/std/sketch'
 | 
			
		||||
import { findKwArg } from '@src/lang/util'
 | 
			
		||||
import type {
 | 
			
		||||
  ArtifactGraph,
 | 
			
		||||
  CallExpressionKw,
 | 
			
		||||
  Expr,
 | 
			
		||||
  ObjectExpression,
 | 
			
		||||
  PathToNode,
 | 
			
		||||
  PipeExpression,
 | 
			
		||||
  Program,
 | 
			
		||||
  VariableDeclaration,
 | 
			
		||||
  VariableDeclarator,
 | 
			
		||||
import {
 | 
			
		||||
  type ArtifactGraph,
 | 
			
		||||
  type CallExpressionKw,
 | 
			
		||||
  type Expr,
 | 
			
		||||
  type ObjectExpression,
 | 
			
		||||
  type PathToNode,
 | 
			
		||||
  type Program,
 | 
			
		||||
  type VariableDeclarator,
 | 
			
		||||
  type ExpressionStatement,
 | 
			
		||||
} from '@src/lang/wasm'
 | 
			
		||||
import type { KclCommandValue } from '@src/lib/commandTypes'
 | 
			
		||||
import type { Selection, Selections } from '@src/lib/selections'
 | 
			
		||||
import { err } from '@src/lib/trap'
 | 
			
		||||
import { isArray } from '@src/lib/utils'
 | 
			
		||||
import {
 | 
			
		||||
  createTagExpressions,
 | 
			
		||||
  modifyAstWithTagsForSelection,
 | 
			
		||||
} from '@src/lang/modifyAst/tagManagement'
 | 
			
		||||
import { deleteNodeInExtrudePipe } from '@src/lang/modifyAst/deleteNodeInExtrudePipe'
 | 
			
		||||
import { deleteTopLevelStatement } from '@src/lang/modifyAst'
 | 
			
		||||
 | 
			
		||||
// Edge Treatment Types
 | 
			
		||||
export enum EdgeTreatmentType {
 | 
			
		||||
@ -164,12 +165,12 @@ export async function modifyAstWithEdgeTreatmentAndTag(
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    // Locate the extrude call
 | 
			
		||||
    const locatedExtrudeDeclarator = locateExtrudeDeclarator(
 | 
			
		||||
    const locatedExtrudeDeclarator = locateVariableWithCallOrPipe(
 | 
			
		||||
      clonedAst,
 | 
			
		||||
      pathToExtrudeNode
 | 
			
		||||
    )
 | 
			
		||||
    if (err(locatedExtrudeDeclarator)) return locatedExtrudeDeclarator
 | 
			
		||||
    const { extrudeDeclarator } = locatedExtrudeDeclarator
 | 
			
		||||
    const { variableDeclarator } = locatedExtrudeDeclarator
 | 
			
		||||
 | 
			
		||||
    // Modify the extrude expression to include this edge treatment expression
 | 
			
		||||
    // CallExpression - no edge treatment
 | 
			
		||||
@ -177,33 +178,33 @@ export async function modifyAstWithEdgeTreatmentAndTag(
 | 
			
		||||
 | 
			
		||||
    let pathToEdgeTreatmentNode: PathToNode
 | 
			
		||||
 | 
			
		||||
    if (extrudeDeclarator.init.type === 'CallExpressionKw') {
 | 
			
		||||
    if (variableDeclarator.init.type === 'CallExpressionKw') {
 | 
			
		||||
      // 1. case when no edge treatment exists
 | 
			
		||||
 | 
			
		||||
      // modify ast with new edge treatment call by mutating the extrude node
 | 
			
		||||
      extrudeDeclarator.init = createPipeExpression([
 | 
			
		||||
        extrudeDeclarator.init,
 | 
			
		||||
      variableDeclarator.init = createPipeExpression([
 | 
			
		||||
        variableDeclarator.init,
 | 
			
		||||
        edgeTreatmentCall,
 | 
			
		||||
      ])
 | 
			
		||||
 | 
			
		||||
      // get path to the edge treatment node
 | 
			
		||||
      pathToEdgeTreatmentNode = getPathToNodeOfEdgeTreatmentLiteral(
 | 
			
		||||
        pathToExtrudeNode,
 | 
			
		||||
        extrudeDeclarator,
 | 
			
		||||
        variableDeclarator,
 | 
			
		||||
        firstTag,
 | 
			
		||||
        parameters
 | 
			
		||||
      )
 | 
			
		||||
      pathToEdgeTreatmentNodes.push(pathToEdgeTreatmentNode)
 | 
			
		||||
    } else if (extrudeDeclarator.init.type === 'PipeExpression') {
 | 
			
		||||
    } else if (variableDeclarator.init.type === 'PipeExpression') {
 | 
			
		||||
      // 2. case when edge treatment exists or extrude in sketch pipe
 | 
			
		||||
 | 
			
		||||
      // mutate the extrude node with the new edge treatment call
 | 
			
		||||
      extrudeDeclarator.init.body.push(edgeTreatmentCall)
 | 
			
		||||
      variableDeclarator.init.body.push(edgeTreatmentCall)
 | 
			
		||||
 | 
			
		||||
      // get path to the edge treatment node
 | 
			
		||||
      pathToEdgeTreatmentNode = getPathToNodeOfEdgeTreatmentLiteral(
 | 
			
		||||
        pathToExtrudeNode,
 | 
			
		||||
        extrudeDeclarator,
 | 
			
		||||
        variableDeclarator,
 | 
			
		||||
        firstTag,
 | 
			
		||||
        parameters
 | 
			
		||||
      )
 | 
			
		||||
@ -330,38 +331,6 @@ export function getEdgeTagCall(
 | 
			
		||||
  return tagCall
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function locateExtrudeDeclarator(
 | 
			
		||||
  node: Program,
 | 
			
		||||
  pathToExtrudeNode: PathToNode
 | 
			
		||||
): { extrudeDeclarator: VariableDeclarator; shallowPath: PathToNode } | Error {
 | 
			
		||||
  const nodeOfExtrudeCall = getNodeFromPath<VariableDeclaration>(
 | 
			
		||||
    node,
 | 
			
		||||
    pathToExtrudeNode,
 | 
			
		||||
    'VariableDeclaration'
 | 
			
		||||
  )
 | 
			
		||||
  if (err(nodeOfExtrudeCall)) return nodeOfExtrudeCall
 | 
			
		||||
 | 
			
		||||
  const { node: extrudeVarDecl } = nodeOfExtrudeCall
 | 
			
		||||
  const extrudeDeclarator = extrudeVarDecl.declaration
 | 
			
		||||
  if (!extrudeDeclarator) {
 | 
			
		||||
    return new Error('Extrude Declarator not found.')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const extrudeInit = extrudeDeclarator?.init
 | 
			
		||||
  if (!extrudeInit) {
 | 
			
		||||
    return new Error('Extrude Init not found.')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (
 | 
			
		||||
    extrudeInit.type !== 'CallExpressionKw' &&
 | 
			
		||||
    extrudeInit.type !== 'PipeExpression'
 | 
			
		||||
  ) {
 | 
			
		||||
    return new Error('Extrude must be a PipeExpression or CallExpressionKw')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return { extrudeDeclarator, shallowPath: nodeOfExtrudeCall.shallowPath }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getPathToNodeOfEdgeTreatmentLiteral(
 | 
			
		||||
  pathToExtrudeNode: PathToNode,
 | 
			
		||||
  extrudeDeclarator: VariableDeclarator,
 | 
			
		||||
@ -484,7 +453,7 @@ function getParameterNameAndValue(
 | 
			
		||||
        : parameters.length.valueAst
 | 
			
		||||
    return { parameterName: 'length', parameterValue }
 | 
			
		||||
  } else {
 | 
			
		||||
    return new Error('Unsupported edge treatment type}')
 | 
			
		||||
    return new Error('Unsupported edge treatment type')
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -614,14 +583,11 @@ export async function deleteEdgeTreatment(
 | 
			
		||||
  selection: Selection
 | 
			
		||||
): Promise<Node<Program> | Error> {
 | 
			
		||||
  /**
 | 
			
		||||
   * Deletes an edge treatment (fillet or chamfer)
 | 
			
		||||
   * from the AST based on the selection.
 | 
			
		||||
   * Handles both standalone treatments
 | 
			
		||||
   * and those within a PipeExpression.
 | 
			
		||||
   * Deletes an edge treatment (fillet or chamfer) from the AST
 | 
			
		||||
   *
 | 
			
		||||
   * Supported cases:
 | 
			
		||||
   * [+] fillet and chamfer
 | 
			
		||||
   * [+] piped and non-piped edge treatments
 | 
			
		||||
   * [+] piped, standalone (assigned and unassigned) edge treatments
 | 
			
		||||
   * [-] delete single tag from array of tags (currently whole expression is deleted)
 | 
			
		||||
   * [-] multiple selections with different edge treatments (currently single selection is supported)
 | 
			
		||||
   */
 | 
			
		||||
@ -632,119 +598,49 @@ export async function deleteEdgeTreatment(
 | 
			
		||||
    return new Error('Selection is not an edge cut')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const { subType: edgeTreatmentType } = artifact
 | 
			
		||||
  if (
 | 
			
		||||
    !edgeTreatmentType ||
 | 
			
		||||
    !['fillet', 'chamfer'].includes(edgeTreatmentType)
 | 
			
		||||
  ) {
 | 
			
		||||
  const { subType } = artifact
 | 
			
		||||
  if (!isEdgeTreatmentType(subType)) {
 | 
			
		||||
    return new Error('Unsupported or missing edge treatment type')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 2. Clone ast and retrieve the VariableDeclarator
 | 
			
		||||
  // 2. Clone ast and retrieve the edge treatment node
 | 
			
		||||
  const astClone = structuredClone(ast)
 | 
			
		||||
  const varDec = getNodeFromPath<VariableDeclarator>(
 | 
			
		||||
    ast,
 | 
			
		||||
    selection?.codeRef?.pathToNode,
 | 
			
		||||
    'VariableDeclarator'
 | 
			
		||||
  )
 | 
			
		||||
  if (err(varDec)) return varDec
 | 
			
		||||
  const edgeTreatmentNode = getNodeFromPath<
 | 
			
		||||
    VariableDeclarator | ExpressionStatement
 | 
			
		||||
  >(astClone, selection?.codeRef?.pathToNode, [
 | 
			
		||||
    'VariableDeclarator',
 | 
			
		||||
    'ExpressionStatement',
 | 
			
		||||
  ])
 | 
			
		||||
  if (err(edgeTreatmentNode)) return edgeTreatmentNode
 | 
			
		||||
 | 
			
		||||
  // 3: Check if edge treatment is in a pipe
 | 
			
		||||
  const inPipe = varDec.node.init.type === 'PipeExpression'
 | 
			
		||||
  // 3: Delete edge treatments
 | 
			
		||||
  // There 3 possible cases:
 | 
			
		||||
  // - piped: const body = extrude(...) |> fillet(...)
 | 
			
		||||
  // - assigned to variables: fillet0001 = fillet(...)
 | 
			
		||||
  // - unassigned standalone statements: fillet(...)
 | 
			
		||||
  // piped and assigned nodes are in the variable declarator
 | 
			
		||||
  // unassigned nodes are in the expression statement
 | 
			
		||||
 | 
			
		||||
  // 4A. Handle standalone edge treatment
 | 
			
		||||
  if (!inPipe) {
 | 
			
		||||
    const varDecPathStep = varDec.shallowPath[1]
 | 
			
		||||
 | 
			
		||||
    if (!isArray(varDecPathStep) || typeof varDecPathStep[0] !== 'number') {
 | 
			
		||||
      return new Error(
 | 
			
		||||
        'Invalid shallowPath structure: expected a number at shallowPath[1][0]'
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const varDecIndex: number = varDecPathStep[0]
 | 
			
		||||
 | 
			
		||||
    // Remove entire VariableDeclarator from the ast
 | 
			
		||||
    astClone.body.splice(varDecIndex, 1)
 | 
			
		||||
    return astClone
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 4B. Handle edge treatment within pipe
 | 
			
		||||
  if (inPipe) {
 | 
			
		||||
    // Retrieve the CallExpression path
 | 
			
		||||
    const callExp =
 | 
			
		||||
      getNodeFromPath<CallExpressionKw>(
 | 
			
		||||
        ast,
 | 
			
		||||
        selection?.codeRef?.pathToNode,
 | 
			
		||||
        'CallExpressionKw'
 | 
			
		||||
      ) ?? null
 | 
			
		||||
    if (err(callExp)) return callExp
 | 
			
		||||
 | 
			
		||||
    const shallowPath = callExp.shallowPath
 | 
			
		||||
 | 
			
		||||
    // Initialize variables to hold the PipeExpression path and callIndex
 | 
			
		||||
    let pipeExpressionPath: PathToNode | null = null
 | 
			
		||||
    let callIndex: number | null = null
 | 
			
		||||
 | 
			
		||||
    // Iterate through the shallowPath to find the PipeExpression and callIndex
 | 
			
		||||
    for (let i = 0; i < shallowPath.length - 1; i++) {
 | 
			
		||||
      const [key, value] = shallowPath[i]
 | 
			
		||||
 | 
			
		||||
      if (key === 'body' && value === 'PipeExpression') {
 | 
			
		||||
        pipeExpressionPath = shallowPath.slice(0, i + 1)
 | 
			
		||||
 | 
			
		||||
        const nextStep = shallowPath[i + 1]
 | 
			
		||||
        if (
 | 
			
		||||
          nextStep &&
 | 
			
		||||
          nextStep[1] === 'index' &&
 | 
			
		||||
          typeof nextStep[0] === 'number'
 | 
			
		||||
        ) {
 | 
			
		||||
          callIndex = nextStep[0]
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        break
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!pipeExpressionPath) {
 | 
			
		||||
      return new Error('PipeExpression not found in path')
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (callIndex === null) {
 | 
			
		||||
      return new Error('Failed to extract CallExpressionKw index')
 | 
			
		||||
    }
 | 
			
		||||
    // Retrieve the PipeExpression node
 | 
			
		||||
    const pipeExpressionNode = getNodeFromPath<PipeExpression>(
 | 
			
		||||
  if (
 | 
			
		||||
    edgeTreatmentNode.node.type === 'ExpressionStatement' || // unassigned
 | 
			
		||||
    (edgeTreatmentNode.node.type === 'VariableDeclarator' && // assigned
 | 
			
		||||
      edgeTreatmentNode.node.init?.type !== 'PipeExpression')
 | 
			
		||||
  ) {
 | 
			
		||||
    // Handle both standalone cases (assigned and unassigned)
 | 
			
		||||
    const deleteResult = deleteTopLevelStatement(
 | 
			
		||||
      astClone,
 | 
			
		||||
      pipeExpressionPath,
 | 
			
		||||
      'PipeExpression'
 | 
			
		||||
      selection.codeRef.pathToNode
 | 
			
		||||
    )
 | 
			
		||||
    if (err(pipeExpressionNode)) return pipeExpressionNode
 | 
			
		||||
 | 
			
		||||
    // Ensure that the PipeExpression.body is an array
 | 
			
		||||
    if (!isArray(pipeExpressionNode.node.body)) {
 | 
			
		||||
      return new Error('PipeExpression body is not an array')
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Remove the CallExpression at the specified index
 | 
			
		||||
    pipeExpressionNode.node.body.splice(callIndex, 1)
 | 
			
		||||
 | 
			
		||||
    // Remove VariableDeclarator if PipeExpression.body is empty
 | 
			
		||||
    if (pipeExpressionNode.node.body.length === 0) {
 | 
			
		||||
      const varDecPathStep = varDec.shallowPath[1]
 | 
			
		||||
      if (!isArray(varDecPathStep) || typeof varDecPathStep[0] !== 'number') {
 | 
			
		||||
        return new Error(
 | 
			
		||||
          'Invalid shallowPath structure: expected a number at shallowPath[1][0]'
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
      const varDecIndex: number = varDecPathStep[0]
 | 
			
		||||
      astClone.body.splice(varDecIndex, 1)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (err(deleteResult)) return deleteResult
 | 
			
		||||
    return astClone
 | 
			
		||||
  } else {
 | 
			
		||||
    const deleteResult = deleteNodeInExtrudePipe(
 | 
			
		||||
      astClone,
 | 
			
		||||
      selection.codeRef.pathToNode
 | 
			
		||||
    )
 | 
			
		||||
    if (err(deleteResult)) return deleteResult
 | 
			
		||||
    return astClone
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return Error('Delete fillets not implemented')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Edit Edge Treatment
 | 
			
		||||
@ -786,7 +682,7 @@ export async function editEdgeTreatment(
 | 
			
		||||
    edgeTreatmentCall.node.arguments[index] = newArg
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let pathToEdgeTreatmentNode = selection?.codeRef?.pathToNode
 | 
			
		||||
  const pathToEdgeTreatmentNode = selection?.codeRef?.pathToNode
 | 
			
		||||
 | 
			
		||||
  return { modifiedAst, pathToEdgeTreatmentNode }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,26 +1,26 @@
 | 
			
		||||
import type { Node } from '@rust/kcl-lib/bindings/Node'
 | 
			
		||||
 | 
			
		||||
import type { PathToNode, Program } from '@src/lang/wasm'
 | 
			
		||||
import { locateExtrudeDeclarator } from '@src/lang/modifyAst/addEdgeTreatment'
 | 
			
		||||
import { locateVariableWithCallOrPipe } from '@src/lang/queryAst'
 | 
			
		||||
import { err } from '@src/lib/trap'
 | 
			
		||||
 | 
			
		||||
export function deleteNodeInExtrudePipe(
 | 
			
		||||
  node: PathToNode,
 | 
			
		||||
  ast: Node<Program>
 | 
			
		||||
  ast: Node<Program>,
 | 
			
		||||
  node: PathToNode
 | 
			
		||||
): Error | void {
 | 
			
		||||
  const pipeIndex = node.findIndex(([_, type]) => type === 'PipeExpression') + 1
 | 
			
		||||
  if (!(node[pipeIndex][0] && typeof node[pipeIndex][0] === 'number')) {
 | 
			
		||||
    return new Error("Couldn't find node to delete in ast")
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const lookup = locateExtrudeDeclarator(ast, node)
 | 
			
		||||
  const lookup = locateVariableWithCallOrPipe(ast, node)
 | 
			
		||||
  if (err(lookup)) {
 | 
			
		||||
    return lookup
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (lookup.extrudeDeclarator.init.type !== 'PipeExpression') {
 | 
			
		||||
  if (lookup.variableDeclarator.init.type !== 'PipeExpression') {
 | 
			
		||||
    return new Error("Couldn't find node to delete in looked up extrusion")
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  lookup.extrudeDeclarator.init.body.splice(node[pipeIndex][0], 1)
 | 
			
		||||
  lookup.variableDeclarator.init.body.splice(node[pipeIndex][0], 1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@ import {
 | 
			
		||||
  createPipeExpression,
 | 
			
		||||
  createPipeSubstitution,
 | 
			
		||||
} from '@src/lang/create'
 | 
			
		||||
import { locateExtrudeDeclarator } from '@src/lang/modifyAst/addEdgeTreatment'
 | 
			
		||||
import { locateVariableWithCallOrPipe } from '@src/lang/queryAst'
 | 
			
		||||
import type { PathToNode, Program } from '@src/lang/wasm'
 | 
			
		||||
import { COMMAND_APPEARANCE_COLOR_DEFAULT } from '@src/lib/commandBarConfigs/modelingCommandConfig'
 | 
			
		||||
import { err } from '@src/lib/trap'
 | 
			
		||||
@ -23,13 +23,13 @@ export function setAppearance({
 | 
			
		||||
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
 | 
			
		||||
  const modifiedAst = structuredClone(ast)
 | 
			
		||||
 | 
			
		||||
  // Locate the call (not necessarily an extrude here)
 | 
			
		||||
  const result = locateExtrudeDeclarator(modifiedAst, nodeToEdit)
 | 
			
		||||
  // Locate the call
 | 
			
		||||
  const result = locateVariableWithCallOrPipe(modifiedAst, nodeToEdit)
 | 
			
		||||
  if (err(result)) {
 | 
			
		||||
    return result
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const declarator = result.extrudeDeclarator
 | 
			
		||||
  const declarator = result.variableDeclarator
 | 
			
		||||
  const call = createCallExpressionStdLibKw(
 | 
			
		||||
    'appearance',
 | 
			
		||||
    createPipeSubstitution(),
 | 
			
		||||
 | 
			
		||||
@ -1175,6 +1175,41 @@ export function getSketchSelectionsFromOperation(
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function locateVariableWithCallOrPipe(
 | 
			
		||||
  ast: Program,
 | 
			
		||||
  pathToNode: PathToNode
 | 
			
		||||
): { variableDeclarator: VariableDeclarator; shallowPath: PathToNode } | Error {
 | 
			
		||||
  const variableDeclarationNode = getNodeFromPath<VariableDeclaration>(
 | 
			
		||||
    ast,
 | 
			
		||||
    pathToNode,
 | 
			
		||||
    'VariableDeclaration'
 | 
			
		||||
  )
 | 
			
		||||
  if (err(variableDeclarationNode)) return variableDeclarationNode
 | 
			
		||||
 | 
			
		||||
  const { node: variableDecl } = variableDeclarationNode
 | 
			
		||||
  const variableDeclarator = variableDecl.declaration
 | 
			
		||||
  if (!variableDeclarator) {
 | 
			
		||||
    return new Error('Variable Declarator not found.')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const initializer = variableDeclarator?.init
 | 
			
		||||
  if (!initializer) {
 | 
			
		||||
    return new Error('Initializer not found.')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (
 | 
			
		||||
    initializer.type !== 'CallExpressionKw' &&
 | 
			
		||||
    initializer.type !== 'PipeExpression'
 | 
			
		||||
  ) {
 | 
			
		||||
    return new Error('Initializer must be a PipeExpression or CallExpressionKw')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    variableDeclarator,
 | 
			
		||||
    shallowPath: variableDeclarationNode.shallowPath,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function findImportNodeAndAlias(
 | 
			
		||||
  ast: Node<Program>,
 | 
			
		||||
  pathToNode: PathToNode
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user