Background & Motivation
Developers writing azd hooks in JavaScript need the same zero-config experience delivered for Python in Phase 1. JavaScript is one of the most popular languages in the Azure ecosystem, and many azd users already have Node.js projects.
Parent Epic: #7435 — Multi-Language Hook Support
Phase 1 PR: #7451 — delivered the extensible framework and Python support
User Story
As an azd user, I want to write hook scripts in JavaScript (.js files) so that I can use my preferred language for pre/post-provisioning and deployment automation.
Existing Infrastructure (from Phase 1)
The following already exist and are shared by all new executors:
HookExecutor interface (pkg/tools/script.go) — Prepare/Execute/Cleanup lifecycle
HookKind constant js (pkg/tools/language/executor.go)
InferKindFromPath() — maps .js→js
- Project discovery (
pkg/tools/language/project_discovery.go) — walk-up for package.json
- IoC-based executor resolution in
hooks_runner.go
node.Cli singleton in cmd/container.go
- Schema support for
kind: js in azure.yaml.json
ExecutionContext with Cwd, EnvVars, BoundaryDir, etc.
Solution Approach
- Create
jsExecutor in pkg/tools/language/js_executor.go implementing HookExecutor
- Prepare():
- Validate Node.js is installed via existing
node.Cli.CheckInstalled() — hard error with ErrorWithSuggestion if missing (include install URL)
- Use
DiscoverProjectFile() with package.json to find project root
- If
package.json found: auto-detect package manager from lock files (npm/pnpm/yarn) using existing node.Cli detection, then run install
- If standalone (no
package.json): skip dependency installation
- Execute():
- With project: run
node script.js with project's node_modules available
- Standalone: run
node script.js directly
- Pass environment variables via
ExecutionContext.EnvVars
- Respect
ExecutionContext.Cwd for working directory
- Respect
ExecutionContext.Interactive for stdin/stdout
- Cleanup(): No-op (no temp files created)
- Register
"js" as named transient in cmd/container.go alongside existing "sh", "pwsh", "python"
- Update
docs/language-hooks.md with JavaScript section (configuration examples, package manager detection, package.json handling)
Acceptance Criteria
Out of Scope
- TypeScript compilation/execution (separate issue)
- Deno or Bun runtime support
- Global npm package installation
Testing Expectations
- Unit tests for
jsExecutor (Prepare, Execute, Cleanup) with mocked node.Cli and CommandRunner
- Unit tests for package manager detection from lock files
- E2E test: JS hook with
package.json → npm install → execute → verify output
- E2E test: standalone JS script → execute without install
- E2E test: missing Node.js → error with suggestion
- E2E test:
continueOnError: true behavior
- E2E test: service-level JS hooks
Constraints
- Must reuse existing
node.Cli singleton — do not create a separate Node wrapper
- Follow the Python executor pattern for consistency (Prepare/Execute/Cleanup lifecycle)
- Inline JavaScript hooks are rejected (file-based only, consistent with Python)
Related Issues
Background & Motivation
Developers writing azd hooks in JavaScript need the same zero-config experience delivered for Python in Phase 1. JavaScript is one of the most popular languages in the Azure ecosystem, and many azd users already have Node.js projects.
Parent Epic: #7435 — Multi-Language Hook Support
Phase 1 PR: #7451 — delivered the extensible framework and Python support
User Story
As an azd user, I want to write hook scripts in JavaScript (
.jsfiles) so that I can use my preferred language for pre/post-provisioning and deployment automation.Existing Infrastructure (from Phase 1)
The following already exist and are shared by all new executors:
HookExecutorinterface (pkg/tools/script.go) — Prepare/Execute/Cleanup lifecycleHookKindconstantjs(pkg/tools/language/executor.go)InferKindFromPath()— maps.js→jspkg/tools/language/project_discovery.go) — walk-up forpackage.jsonhooks_runner.gonode.Clisingleton incmd/container.gokind: jsinazure.yaml.jsonExecutionContextwith Cwd, EnvVars, BoundaryDir, etc.Solution Approach
jsExecutorinpkg/tools/language/js_executor.goimplementingHookExecutornode.Cli.CheckInstalled()— hard error withErrorWithSuggestionif missing (include install URL)DiscoverProjectFile()withpackage.jsonto find project rootpackage.jsonfound: auto-detect package manager from lock files (npm/pnpm/yarn) using existingnode.Clidetection, then run installpackage.json): skip dependency installationnode script.jswith project'snode_modulesavailablenode script.jsdirectlyExecutionContext.EnvVarsExecutionContext.Cwdfor working directoryExecutionContext.Interactivefor stdin/stdout"js"as named transient incmd/container.goalongside existing"sh","pwsh","python"docs/language-hooks.mdwith JavaScript section (configuration examples, package manager detection,package.jsonhandling)Acceptance Criteria
.jshook scripts auto-detected and executed via Node.jspackage.jsondiscovered via walk-up, dependencies installed before execution.jsscripts (nopackage.json) execute without dependency installationErrorWithSuggestionkind: jsexplicit override works in azure.yaml"js"in container.goOut of Scope
Testing Expectations
jsExecutor(Prepare, Execute, Cleanup) with mockednode.CliandCommandRunnerpackage.json→ npm install → execute → verify outputcontinueOnError: truebehaviorConstraints
node.Clisingleton — do not create a separate Node wrapperRelated Issues