Skip to content

feat: explorer codelens nav + template resource locations#1617

Open
megha-narayanan wants to merge 14 commits into
aws:feat/cdk-lspfrom
megha-narayanan:feat/explorer-codelens-nav
Open

feat: explorer codelens nav + template resource locations#1617
megha-narayanan wants to merge 14 commits into
aws:feat/cdk-lspfrom
megha-narayanan:feat/explorer-codelens-nav

Conversation

@megha-narayanan

Copy link
Copy Markdown

Builds on #1592, which surfaced display-only CodeLens entries for the CFN
resources each construct produces. This PR makes them clickable. Selecting a
lens jumps to the resource's definition in the synthesized CloudFormation template.

Features:

  • Clickable navigation — each lens carries an openResource command that opens
    the resource's template file at its logical-ID line.
  • Multi-resource picker — constructs producing several resources show a QuickPick;
    single-resource constructs open directly.
  • Positional templateFile resolution (cloud-assembly-api)buildConstructTree
    threads the owning template through the tree, switching at NestedStack boundaries.
  • Clearer titlesCreates AWS::S3::Bucket, or
    Creates 3 resources: AWS::S3::Bucket, AWS::S3::BucketPolicy, AWS::KMS::Key.

Checklist

  • This change contains a major version upgrade for a dependency and I confirm all breaking changes are addressed
    • Release notes for the new version:

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

Decorate every CFN-resource ConstructNode with templateFile: the absolute
path of the template that declares its logicalId, descending into
CDK-managed nested stacks via the aws:asset:path resource metadata (the
same link CDK's deploy/diff uses). This is the data the clickable CodeLens
needs to open a resource in its synthesized template.

Self-contained in cdk-explorer: a recursive walk over stacksRecursively
builds a flat logicalId -> templateFile map; no cloud-assembly-api or
toolkit-lib changes.
Add findLogicalIdPosition(templateText, logicalId): the 0-based position
of a resource's definition key within its synthesized template. Anchors on
the "<id>": key, so Ref/GetAtt/DependsOn value occurrences of the same id
are not matched. Pairs with the templateFile resolved per node to give the
clickable CodeLens its navigation target.
Add resourceTarget(node) -> { uri, range }: reads the node's resolved
template, locates its logicalId via findLogicalIdPosition, and returns a
file:// uri + zero-width range to reveal. Returns undefined when the node
has no template/logicalId or the id is not found -- the single not-navigable
signal callers filter on. Joins the templateFile (Bit 1) and locator (Bit 2)
into the target the clickable CodeLens will carry.
…ource lenses

A lens for a single resource now carries command cdkExplorer.openResource
with its { uri, range } navigation target as the argument, so a client can
open the template at the resource. Multiple resources on one line stay
title-only (picker comes later), as does a single resource whose target
cannot be resolved. Server-side only -- nothing executes the command until
the client registers a handler.
…cker

A lens now carries an array of resource choices ({ label, target }) under
cdkExplorer.openResource. A single resource is a one-element array the client
opens directly; multiple resources on one line (an L2 fanning out) let the
client show a picker. Unresolvable resources are dropped, and a line where
none resolve stays title-only.
…xes)

A1: key templateFiles by stack hierarchicalId, not a flat logicalId map --
logical IDs are stack-relative so same-shape stacks (prod/dev/regional) would
otherwise collide last-write-wins and navigate to the wrong template.
B1: type the template walk via local CfnTemplate/CfnResource (no any).
B2: narrow resourceTarget to { templateFile?, logicalId: string }, dropping
the impossible-state guard since commandFor only passes resolved resources.
Lens titles are type-forward and drop the noisy hashed logical ID:
  single -> 'Creates AWS::S3::Bucket'
  multi  -> 'Creates N resources: <type>, <type>, ...' (now shows types)
The picker carries each choice as a QuickPick item (label = CFN type,
description = friendly construct path, e.g. 'MyStack/MyBucket') instead of
an opaque logical-ID hash. Also drops the unused ResourceChoice export and
the ResourceLensInfo relabel allocation (review nits C1/C2).
…its C3/C4)

C3: replace the dynamic RegExp (+escapeRegExp guarding an impossible state,
since logical IDs are [A-Za-z0-9]) with a plain trimStart/startsWith key match
plus a next-char colon check. Same anti-false-match and prefix-collision
guarantees, no regex.
C4: trim JSDoc that restated the code.
… fallback

Fixes the parent-vs-nested logical-ID collision: a NestedStack resets the
logical-ID namespace, so a parent resource and a resource in its own nested
stack can share an id and the per-stack logical-ID map resolved the parent to
the nested template. Key primarily by construct path (globally unique, from
aws:cdk:path metadata) which disambiguates the twins; fall back to the
per-stack logical-ID map when path metadata is off (--no-path-metadata), so
resolution never regresses. Fixtures now emit aws:cdk:path (matching real
synth); adds collision + fallback regression tests.
missing/unparseable nested template skips only its subtree instead of aborting
the entire readAssembly (matches the file's graceful-degradation contract).
(depth-2) resolution test and an unreadable-nested-template guard test.
…onstructTree

Move templateFile resolution from cdk-explorer into the core tree builder.
buildConstructTree now threads the active CloudFormation template down the
construct tree, switching to the nested template at each NestedStack boundary
(the *.NestedStack construct + its sibling NestedStackResource's aws:asset:path).

Because resolution is positional (by tree position, not by logical ID), it is
correct for parent/nested resources that share a stack-relative logical ID --
with no per-stack id map and no dependence on aws:cdk:path/path metadata. This
eliminates the --no-path-metadata collision the explorer's id-keyed index had.

cdk-explorer drops buildTemplateFileIndex + its maps and just consumes the
templateFile field. Test fixtures now emit the faithful NestedStack topology
(sibling NestedStackResource node) that real synth produces.
…arden types

- Top-level/Stage stacks read through the artifact's cached, fail-loud
  template getter instead of a shadow-parsing loadTemplate (which swallowed
  errors and kept a second cache).
- Detect NestedStack boundaries via the sibling AWS::CloudFormation::Stack
  node rather than the construct fqn, so jsii-published NestedStack subclasses
  (fqn not ending in .NestedStack) are handled and don't mis-inherit the parent.
- Resolve the nested asset path against the owner stack's assembly directory
  (the Stage sub-assembly for staged stacks), not the root assembly.
- Thread a single both-or-neither TemplateScope through the walk (rename from
  NestedTemplateScope) with a shared NO_TEMPLATE seed; drop now-dead
  WalkContext.assembly and RawTreeNode.constructInfo.
- Add direct caa nested-stack tests (resolved, missing/unreadable, no asset
  metadata, parent/nested logical-id twin, jsii-subclass detection).
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@megha-narayanan megha-narayanan marked this pull request as ready for review June 11, 2026 16:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants