Skip to content

feat: migrate wizard question tree to JSON-driven dynamic templates#15560

Open
qfai wants to merge 3 commits intodevfrom
feature/dynamic-template-json
Open

feat: migrate wizard question tree to JSON-driven dynamic templates#15560
qfai wants to merge 3 commits intodevfrom
feature/dynamic-template-json

Conversation

@qfai
Copy link
Contributor

@qfai qfai commented Mar 20, 2026

Summary

Migrate all wizard question tree nodes from hardcoded TypeScript to JSON configuration files, enabling dynamic template release without code changes.

Changes

New JSON node files (templates/src/ui/)

  • Root.ts → RootNode.json — Project type selection (DA, CEA, Teams, Office Add-in, etc.)
  • da.ts → daNode.json — Declarative Agent sub-tree with FeatureFlag support
  • officeAddin.ts → officeAddinNode.json — Office Add-in sub-tree
  • graphConnector.ts → graphConnectorNode.json — Graph Connector sub-tree

Enhanced constructNode.ts

  • FeatureFlag filtering — JSON options can specify a feature flag; disabled flags are filtered out
  • icon and groupName support — VSCode-specific icons and option grouping
  • loadJsonNode() helper — Loads JSON from cache (~/.fx/ui/) or bundled fallback
  • Lazy imports to avoid circular dependencies
  • [Dynamic Template] logging — Shows which JSON file was loaded and from where (cache vs bundled)

New rootNode.ts

  • Follows existing ceaNode/ eamsNode loader pattern
  • Cache → bundled fallback mechanism

Simplified createRootNode.ts

  • Replaced hardcoded staticOptions + children with getRootProjectTypeNode()
  • Removed direct imports of sub-tree TypeScript modules

Unit Tests

  • 24 new tests covering: JSON parsing, icon handling, groupName, featureFlag filtering, condition handling, recursive children, skipSingleOption, node references, data property mapping

Impact

  • No UI change — Verified with dual-instance Playwright comparison (stable marketplace vs local dev build), 14/14 wizard steps identical
  • No scaffold change — Generated project structure matches stable version
  • Future template CRUD — Only requires JSON changes + template publish (metadata.zip), no code release needed
  • metadata.zip pipeline — All 6 JSON files automatically included via existing generateRelease.js

Verification

  • ✅ Unit tests: 24/24 passing
  • ✅ ESLint: no errors
  • ✅ Duel verify (UI wizard): 14/14 PASS
  • ✅ Duel verify (scaffold): DA No Action generates identical structure

Work item

link

Migrate all wizard question tree nodes from hardcoded TypeScript to JSON configuration
files, enabling dynamic template release without code changes.

Changes:
- Create rootNode.json for project type selection (DA, CEA, Teams, Office Add-in, etc.)
- Create daNode.json for Declarative Agent sub-tree with featureFlag support
- Create officeAddinNode.json for Office Add-in sub-tree
- Create graphConnectorNode.json for Graph Connector sub-tree
- Enhance constructNode.ts with featureFlag filtering, icon/groupName support,
  loadJsonNode helper, and all new node references (lazy imports to avoid cycles)
- Add rootNode.ts loader following existing ceaNode/teamsNode pattern
- Simplify createRootNode.ts to use JSON-loaded root node
- Add 24 unit tests for constructNode and rootNode

The JSON files are included in metadata.zip and distributed via the existing
template publish/fetch pipeline. Future template CRUD only requires JSON changes
+ template publish, no code release needed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Contributor

@tecton tecton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM and left some comments. Also, related strings could be moved to metadata in following PR.

}

/** Map of feature flag names (as stored in JSON) to FeatureFlags enum values */
const featureFlagMap: Record<string, FeatureFlag> = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be redundant?

// Set appropriate onDidSelection callback
if (jsonObject.data.name === QuestionNames.WithPlugin) {
// Lazy import to avoid circular dependency
const { setTemplateNameAndGC } = require("./vsc/daProjectTypeNode");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to use import instead of require.

return foundryNode(jsonObject.condition as any);

// Sub-tree nodes loaded from JSON (lazy imports to avoid circular dependency)
case "ceaNode": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would be the benefit of loading sub-tree nodes dynamically instead of a whole tree node loaded from file? It can be separated in source files and combined into a JSON file to be loaded.

…simplify featureFlag

1. Merge sub-tree JSONs into single wizardNode.json: root + DA + graphConnector +
   officeAddin are combined at build time (wizard.ts). Source files remain separate
   for maintainability. CEA and Teams nodes are still separate (pre-existing pattern).

2. Replace require() with static imports: moved setTemplateNameAndGC to
   CapabilityOptions.ts to break circular dependency. ceaNode/teamsNode loaded via
   loadJsonNode (same file, no circular import).

3. Remove redundant featureFlagMap: replaced with dynamic lookup on FeatureFlags class.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants