Skip to content

Read typespec-ts client user-agent version dynamically from package.json#4859

Open
xirzec wants to merge 7 commits into
mainfrom
xirzec-remove-version-user-provided-flag
Open

Read typespec-ts client user-agent version dynamically from package.json#4859
xirzec wants to merge 7 commits into
mainfrom
xirzec-remove-version-user-provided-flag

Conversation

@xirzec

@xirzec xirzec commented Jul 10, 2026

Copy link
Copy Markdown
Member

Why

The @azure-tools/typespec-ts emitter burned the package-details.version emitter option into the generated client's user-agent string. Once a package is generated, that version is really owned by the package's own package.json, so hardcoding it forced a whole machinery (the //metadata.constantPaths userAgentInfo entries) to exist purely so dev-tool could rewrite the burned-in constant on every release. This PR removes that coupling and simplifies the version handling along the way.

What changed

1. Stop defaulting package-details.version in the datamodel; default at the point of use.
The version was previously defaulted to 1.0.0-beta.1 during option transform, and a separate isVersionUserProvided flag was carried around so the README logic could tell "user set it" from "we defaulted it." That flag existed for exactly one consumer. Now version stays undefined when unset and each consumer (package.json, CHANGELOG, README, user-agent) applies its own default locally, so isVersionUserProvided is gone entirely.

2. Emit code that reads the version dynamically at runtime.
Instead of interpolating a literal version into the generated source, buildUserAgentOptions now emits a self-referencing JSON import and reads pkgJson.version:

import pkgJson from "@azure/foo/package.json" with { type: "json" };
...
const userAgentInfo = `azsdk-js-foo/${pkgJson.version}`;

The self-reference (<packageName>/package.json) relies on the "./package.json" export the emitted package already declares, so the specifier resolves identically from src and from the built dist output regardless of directory depth. The import is deduped per file and only emitted for the api context (the classical-client prefix never surfaced userAgentInfo).

3. Remove the now-obsolete constantPaths plumbing.
With no literal version in the source, there is nothing for dev-tool to bump, so the //metadata.constantPaths userAgentInfo generation and the clientContextPaths option threaded through buildPackageFile / updatePackageFile / buildAzureMonorepoPackage (plus the getRelativeContextPaths helper) are dead. All removed.

Notes for reviewers

  • Emitter type-checks; test-next (252) and unit-modular (657) all pass. The modular snapshot scenarios don't set package-details, so there is zero snapshot churn.
  • One thing worth a careful look / a spector run before merge: I verified the emitted import shape and emitter-side behavior, but did not run a full downstream azure-sdk-for-js build (tshy + api-extractor) to confirm the JSON import compiles end to end in a real generated package. The syntax and module resolution are correct, but that integration path is out of scope for this environment.
  • ts-morph gotcha handled: the import attribute value is passed unquoted (value: "json") because the writer auto-quotes it.

xirzec and others added 3 commits July 9, 2026 20:57
Stop defaulting package-details.version in the data model so an unset version stays undefined; each consumer (package.json, CHANGELOG, README, user-agent) now applies the 1.0.0-beta.1 default locally. This makes the derived isVersionUserProvided flag redundant, so it is removed from the PackageDetails interface, emitter option schema, and docs.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The client user-agent version was burned into the generated source from the emitter's package-details.version option. Since a generated package's version is owned by its package.json, emit a self-referencing JSON import (import pkgJson from '<name>/package.json' with { type: 'json' }) and interpolate pkgJson.version at runtime instead. Uses the package's existing './package.json' export so the specifier is identical in src and dist.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The client user-agent version is now read dynamically from package.json at runtime, so there is no burned-in version constant for dev-tool to bump. Remove the //metadata.constantPaths userAgentInfo generation, the clientContextPaths option threaded through buildPackageFile/updatePackageFile/buildAzureMonorepoPackage, and the getRelativeContextPaths helper, along with the now-obsolete unit tests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@microsoft-github-policy-service microsoft-github-policy-service Bot added the emitter:typescript Issues for @azure-tools/typespec-ts emitter label Jul 10, 2026
@azure-sdk-automation

azure-sdk-automation Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

All changed packages have been documented.

  • @azure-tools/typespec-ts
Show changes

@azure-tools/typespec-ts - internal ✏️

Read the client user-agent version dynamically from the generated package.json at runtime (via the package's own ./package.json export) instead of hardcoding the emitter's package-details.version into the generated source. The generated config/tsconfig.src.*.json files now set resolveJsonModule: true so the JSON import type-checks across compiler versions.

@azure-tools/typespec-ts - internal ✏️

Remove the derived isVersionUserProvided field from PackageDetails and stop defaulting package-details.version in the data model. The default (1.0.0-beta.1) is now applied at each consumption site instead.

@azure-tools/typespec-ts - internal ✏️

Remove the now-obsolete //metadata.constantPaths userAgentInfo generation and the clientContextPaths plumbing that fed it. Since the user-agent version is now read dynamically from package.json at runtime, there is no burned-in version constant for dev-tool to bump.

@pkg-pr-new

pkg-pr-new Bot commented Jul 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@azure-tools/typespec-ts@4859

commit: 212b380

@azure-sdk-automation

Copy link
Copy Markdown
Contributor

You can try these changes here

🛝 Playground 🌐 Website

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

⚡ Benchmark Results

⚠️ 18 metric(s) regressed above the +5% threshold:

Metric Baseline Current Change
total 🔴 1.01s 🔴 1.08s +6.4% 🔴
loader 🟡 305.6ms 🟡 323.0ms +5.7% 🔴
resolver 🟢 40.1ms 🟢 42.8ms +6.7% 🔴
checker 🟡 364.2ms 🟡 391.5ms +7.5% 🔴
validation 🟢 84.6ms 🟢 89.0ms +5.2% 🔴
linter 🟡 215.2ms 🟡 228.3ms +6.1% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/no-header-explode 🔴 26.7ms 🔴 28.4ms +6.5% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/no-query-explode 🔴 27.8ms 🔴 29.7ms +6.9% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/response-schema-problem 🔴 32.7ms 🔴 35.2ms +7.8% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-agent-base-type-child-resources 🔴 21.1ms 🔴 22.1ms +5.0% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/lro-location-header 🟡 19.7ms 🔴 21.2ms +7.9% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-response-body 🔴 29.8ms 🔴 32.3ms +8.2% 🔴
emit 🔴 6.04s 🔴 6.42s +6.4% 🔴
 ↳ emit/@azure-tools/typespec-autorest 🟢 125.4ms 🟢 132.7ms +5.8% 🔴
 ↳ emit/@azure-tools/typespec-python 🔴 2.25s 🔴 2.40s +6.5% 🔴
 ↳ emit/@typespec/http-client-js 🔴 571.0ms 🔴 605.1ms +6.0% 🔴
 ↳ emit/@typespec/openapi3 🟢 107.9ms 🟢 113.8ms +5.4% 🔴
 ↳ emit/@typespec/openapi3/compute 🟢 93.8ms 🟢 98.8ms +5.4% 🔴
Full details – comparing b657a2e vs baseline rolling-baseline-1a09577-3e6a2b2 (rolling baseline (20 main runs))
Metric Baseline Current Change
total 🔴 1.01s 🔴 1.08s +6.4% 🔴
loader 🟡 305.6ms 🟡 323.0ms +5.7% 🔴
resolver 🟢 40.1ms 🟢 42.8ms +6.7% 🔴
checker 🟡 364.2ms 🟡 391.5ms +7.5% 🔴
validation 🟢 84.6ms 🟢 89.0ms +5.2% 🔴
 ↳ validation/@azure-tools/typespec-azure-core 🟡 12.6ms 🟡 13.3ms +5.6%
 ↳ validation/@typespec/http 🟡 15.5ms 🟡 16.4ms +6.1%
 ↳ validation/@typespec/rest 🟢 1.6ms 🟢 1.8ms +9.4%
 ↳ validation/@typespec/versioning 🔴 50.9ms 🔴 53.4ms +4.7%
 ↳ validation/compiler 🟢 3.8ms 🟢 4.1ms +6.1%
linter 🟡 215.2ms 🟡 228.3ms +6.1% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/auth-required 🟢 0.1ms 🟢 0.1ms +6.9%
 ↳ linter/@azure-tools/typespec-azure-core/bad-record-type 🟢 0.6ms 🟢 0.6ms +3.4%
 ↳ linter/@azure-tools/typespec-azure-core/byos 🟢 8.4ms 🟢 8.9ms +6.6%
 ↳ linter/@azure-tools/typespec-azure-core/casing-style 🟢 1.5ms 🟢 1.5ms +5.2%
 ↳ linter/@azure-tools/typespec-azure-core/composition-over-inheritance 🟢 0.2ms 🟢 0.2ms +6.6%
 ↳ linter/@azure-tools/typespec-azure-core/documentation-required 🟢 2.0ms 🟢 2.1ms +5.2%
 ↳ linter/@azure-tools/typespec-azure-core/friendly-name 🟢 1.4ms 🟢 1.5ms +5.4%
 ↳ linter/@azure-tools/typespec-azure-core/key-visibility-required 🟢 0.4ms 🟢 0.4ms +7.0%
 ↳ linter/@azure-tools/typespec-azure-core/known-encoding 🟢 0.5ms 🟢 0.5ms +5.2%
 ↳ linter/@azure-tools/typespec-azure-core/long-running-polling-operation-required 🟢 0.8ms 🟢 0.9ms +4.8%
 ↳ linter/@azure-tools/typespec-azure-core/no-case-mismatch 🟢 0.7ms 🟢 0.7ms +4.2%
 ↳ linter/@azure-tools/typespec-azure-core/no-closed-literal-union 🟢 1.0ms 🟢 1.0ms +5.1%
 ↳ linter/@azure-tools/typespec-azure-core/no-enum 🟢 0.2ms 🟢 0.3ms +5.8%
 ↳ linter/@azure-tools/typespec-azure-core/no-error-status-codes 🟢 0.3ms 🟢 0.3ms +6.2%
 ↳ linter/@azure-tools/typespec-azure-core/no-explicit-routes-resource-ops 🟢 0.2ms 🟢 0.2ms +8.7%
 ↳ linter/@azure-tools/typespec-azure-core/no-format 🟢 0.8ms 🟢 0.9ms +3.4%
 ↳ linter/@azure-tools/typespec-azure-core/no-generic-numeric 🟢 0.8ms 🟢 0.9ms +6.1%
 ↳ linter/@azure-tools/typespec-azure-core/no-header-explode 🔴 26.7ms 🔴 28.4ms +6.5% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/no-legacy-usage 🟢 2.1ms 🟢 2.1ms +3.0%
 ↳ linter/@azure-tools/typespec-azure-core/no-multiple-discriminator 🟢 0.2ms 🟢 0.3ms +9.1%
 ↳ linter/@azure-tools/typespec-azure-core/no-nullable 🟢 0.4ms 🟢 0.4ms +7.2%
 ↳ linter/@azure-tools/typespec-azure-core/no-offsetdatetime 🟢 2.0ms 🟢 2.1ms +4.9%
 ↳ linter/@azure-tools/typespec-azure-core/no-openapi 🟢 2.3ms 🟢 2.4ms +2.1%
 ↳ linter/@azure-tools/typespec-azure-core/no-private-usage 🟢 3.2ms 🟢 3.3ms +4.4%
 ↳ linter/@azure-tools/typespec-azure-core/no-query-explode 🔴 27.8ms 🔴 29.7ms +6.9% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/no-response-body 🔴 33.3ms 🔴 34.9ms +4.6%
 ↳ linter/@azure-tools/typespec-azure-core/no-rest-library-interfaces 🟢 0.1ms 🟢 0.1ms +19.4%
 ↳ linter/@azure-tools/typespec-azure-core/no-route-parameter-name-mismatch 🟢 7.7ms 🟢 8.2ms +6.8%
 ↳ linter/@azure-tools/typespec-azure-core/no-rpc-path-params 🟢 0.4ms 🟢 0.4ms +8.3%
 ↳ linter/@azure-tools/typespec-azure-core/no-string-discriminator 🟢 0.1ms 🟢 0.2ms +7.1%
 ↳ linter/@azure-tools/typespec-azure-core/no-unknown 🟢 0.3ms 🟢 0.4ms +15.8%
 ↳ linter/@azure-tools/typespec-azure-core/no-unnamed-union 🟢 0.8ms 🟢 0.8ms +4.9%
 ↳ linter/@azure-tools/typespec-azure-core/operation-missing-api-version 🟢 0.4ms 🟢 0.4ms +4.2%
 ↳ linter/@azure-tools/typespec-azure-core/request-body-problem 🟢 0.5ms 🟢 0.5ms +4.4%
 ↳ linter/@azure-tools/typespec-azure-core/require-versioned 🟢 0.1ms 🟢 0.1ms +8.4%
 ↳ linter/@azure-tools/typespec-azure-core/response-schema-problem 🔴 32.7ms 🔴 35.2ms +7.8% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/rpc-operation-request-body 🟢 0.7ms 🟢 0.7ms +5.1%
 ↳ linter/@azure-tools/typespec-azure-core/spread-discriminated-model 🟢 0.5ms 🟢 0.5ms +2.6%
 ↳ linter/@azure-tools/typespec-azure-core/use-standard-names 🟢 7.6ms 🟢 8.1ms +6.6%
 ↳ linter/@azure-tools/typespec-azure-core/use-standard-operations 🟢 0.3ms 🟢 0.3ms +5.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-agent-base-type-child-resources 🔴 21.1ms 🔴 22.1ms +5.0% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-agent-base-type-lifecycle-operations 🟢 0.3ms 🟢 0.3ms +4.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-common-types-version 🟡 12.1ms 🟡 12.8ms +6.1%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-custom-resource-no-key 🟢 0.2ms 🟢 0.2ms +5.4%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-custom-resource-usage-discourage 🟢 0.1ms 🟢 0.1ms +5.9%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-delete-operation-response-codes 🟢 5.0ms 🟢 4.6ms -8.6%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-no-path-casing-conflicts 🟡 17.0ms 🟡 17.9ms +5.6%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-no-record 🟢 0.6ms 🟢 0.7ms +4.9%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-post-operation-response-codes 🟢 1.4ms 🟢 1.5ms +8.0%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-put-operation-response-codes 🟢 0.2ms 🟢 0.2ms -2.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-action-no-segment 🟢 0.4ms 🟢 0.5ms +2.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-duplicate-property 🟢 0.4ms 🟢 0.4ms +7.9%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-interface-requires-decorator 🟢 0.1ms 🟢 0.1ms -3.0%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-invalid-action-verb 🟢 0.2ms 🟢 0.2ms +5.6%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-invalid-envelope-property 🟢 0.3ms 🟢 0.3ms +4.5%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-invalid-version-format 🟢 0.2ms 🟢 0.2ms +3.6%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-key-invalid-chars 🟢 0.5ms 🟢 0.5ms +3.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-name-pattern 🟢 0.1ms 🟢 0.2ms +11.2%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-operation 🟢 0.5ms 🟢 0.6ms +4.9%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-operation-response 🟢 9.0ms 🟢 9.4ms +4.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-patch 🟢 0.8ms 🟢 0.9ms +7.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-path-segment-invalid-chars 🟢 0.4ms 🟢 0.4ms +3.4%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-provisioning-state 🟢 0.4ms 🟢 0.5ms +5.7%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/beyond-nesting-levels 🟢 0.2ms 🟢 0.2ms +6.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/empty-updateable-properties 🟢 0.4ms 🟢 0.4ms +2.4%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/improper-subscription-list-operation 🟢 0.1ms 🟢 0.1ms +5.9%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/lro-location-header 🟡 19.7ms 🔴 21.2ms +7.9% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/missing-operations-endpoint 🟢 0.1ms 🟢 0.2ms +7.2%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/missing-x-ms-identifiers 🟢 1.0ms 🟢 1.0ms +3.0%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-empty-model 🟢 0.3ms 🟢 0.3ms +7.7%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-override-props 🟢 0.3ms 🟢 0.3ms +6.2%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-reserved-resource-property 🟢 0.2ms 🟢 0.2ms +1.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-resource-delete-operation 🟢 0.5ms 🟢 0.5ms +7.5%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-response-body 🔴 29.8ms 🔴 32.3ms +8.2% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/patch-envelope 🟢 0.4ms 🟢 0.4ms +6.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/resource-name 🟢 0.4ms 🟢 0.5ms +3.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/secret-prop 🟢 5.4ms 🟢 5.8ms +8.0%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/unsupported-type 🟢 0.7ms 🟢 0.7ms +0.5%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/version-progression 🟢 0.3ms 🟢 0.3ms +2.7%
 ↳ linter/@azure-tools/typespec-client-generator-core/property-name-conflict 🟢 2.3ms 🟢 2.4ms +4.9%
 ↳ linter/@azure-tools/typespec-client-generator-core/require-client-suffix 🟢 1.7ms 🟢 1.8ms +5.6%
emit 🔴 6.04s 🔴 6.42s +6.4% 🔴
 ↳ emit/@Azure-Tools 🟢 0.0ms 🟢 0.0ms +0.0%
 ↳ emit/@azure-tools/typespec-autorest 🟢 125.4ms 🟢 132.7ms +5.8% 🔴
 ↳ emit/@azure-tools/typespec-python 🔴 2.25s 🔴 2.40s +6.5% 🔴
 ↳ emit/@typespec 🟢 0.0ms 🟢 0.0ms +0.0%
 ↳ emit/@typespec/http-client-js 🔴 571.0ms 🔴 605.1ms +6.0% 🔴
 ↳ emit/@typespec/openapi3 🟢 107.9ms 🟢 113.8ms +5.4% 🔴
 ↳ emit/@typespec/openapi3/compute 🟢 93.8ms 🟢 98.8ms +5.4% 🔴
 ↳ emit/@typespec/openapi3/write 🟢 13.8ms 🟢 14.6ms +6.0%

Averaged across 3 specs (azure-arm-resource-manager, azure-core-dataplane, azure-full).
Threshold: changes > ±5% are highlighted.
🟢 Fast · 🟡 Moderate (stages >200ms, rules >10ms) · 🔴 Slow (stages >400ms, rules >20ms)

xirzec and others added 2 commits July 10, 2026 14:01
…-user-provided-flag

# Conflicts:
#	packages/typespec-ts/src/modular/helpers/client-helpers.ts
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@xirzec xirzec marked this pull request as ready for review July 10, 2026 19:11
@microsoft-github-policy-service microsoft-github-policy-service Bot added the meta:website TypeSpec.io updates label Jul 10, 2026
xirzec and others added 2 commits July 10, 2026 16:15
Address CI failures: run prettier on the merged build-azure-monorepo-package.ts and client-helpers.test.ts, and commit the regenerated typespec-ts README emitter-options table (column widths shrank once isVersionUserProvided was removed).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…json import

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

Copilot-Session: 9d488e2f-cc7e-4b97-84d5-5c6adde5f75e
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

emitter:typescript Issues for @azure-tools/typespec-ts emitter meta:website TypeSpec.io updates

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant