@azure-tools/typespec-ts 0.56.0-dev.3 → 0.56.0-dev.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +31 -21
  3. package/dist/src/context-manager.d.ts +17 -0
  4. package/dist/src/context-manager.d.ts.map +1 -1
  5. package/dist/src/context-manager.js +21 -0
  6. package/dist/src/context-manager.js.map +1 -1
  7. package/dist/src/framework/declaration.d.ts.map +1 -1
  8. package/dist/src/framework/declaration.js +6 -20
  9. package/dist/src/framework/declaration.js.map +1 -1
  10. package/dist/src/framework/hooks/sdk-types.d.ts +9 -0
  11. package/dist/src/framework/hooks/sdk-types.d.ts.map +1 -1
  12. package/dist/src/framework/hooks/sdk-types.js +13 -0
  13. package/dist/src/framework/hooks/sdk-types.js.map +1 -1
  14. package/dist/src/framework/source-file-batch.d.ts +35 -0
  15. package/dist/src/framework/source-file-batch.d.ts.map +1 -0
  16. package/dist/src/framework/source-file-batch.js +128 -0
  17. package/dist/src/framework/source-file-batch.js.map +1 -0
  18. package/dist/src/index.js +12 -12
  19. package/dist/src/index.js.map +1 -1
  20. package/dist/src/interfaces.d.ts +0 -2
  21. package/dist/src/interfaces.d.ts.map +1 -1
  22. package/dist/src/interfaces.js.map +1 -1
  23. package/dist/src/lib.d.ts +15 -2
  24. package/dist/src/lib.d.ts.map +1 -1
  25. package/dist/src/lib.js +22 -11
  26. package/dist/src/lib.js.map +1 -1
  27. package/dist/src/metadata/build-package-file.d.ts +2 -4
  28. package/dist/src/metadata/build-package-file.d.ts.map +1 -1
  29. package/dist/src/metadata/build-package-file.js +2 -17
  30. package/dist/src/metadata/build-package-file.js.map +1 -1
  31. package/dist/src/metadata/build-readme-file.d.ts.map +1 -1
  32. package/dist/src/metadata/build-readme-file.js +17 -70
  33. package/dist/src/metadata/build-readme-file.js.map +1 -1
  34. package/dist/src/metadata/build-ts-config.d.ts.map +1 -1
  35. package/dist/src/metadata/build-ts-config.js +12 -0
  36. package/dist/src/metadata/build-ts-config.js.map +1 -1
  37. package/dist/src/metadata/package-json/build-azure-monorepo-package.d.ts +0 -1
  38. package/dist/src/metadata/package-json/build-azure-monorepo-package.d.ts.map +1 -1
  39. package/dist/src/metadata/package-json/build-azure-monorepo-package.js +0 -14
  40. package/dist/src/metadata/package-json/build-azure-monorepo-package.js.map +1 -1
  41. package/dist/src/metadata/render-template.d.ts +24 -0
  42. package/dist/src/metadata/render-template.d.ts.map +1 -0
  43. package/dist/src/metadata/render-template.js +148 -0
  44. package/dist/src/metadata/render-template.js.map +1 -0
  45. package/dist/src/metadata/test/build-recorded-client.d.ts.map +1 -1
  46. package/dist/src/metadata/test/build-recorded-client.js +1 -7
  47. package/dist/src/metadata/test/build-recorded-client.js.map +1 -1
  48. package/dist/src/metadata/test/build-sample-test.d.ts.map +1 -1
  49. package/dist/src/metadata/test/build-sample-test.js +1 -4
  50. package/dist/src/metadata/test/build-sample-test.js.map +1 -1
  51. package/dist/src/metadata/test/build-snippets.d.ts.map +1 -1
  52. package/dist/src/metadata/test/build-snippets.js +2 -4
  53. package/dist/src/metadata/test/build-snippets.js.map +1 -1
  54. package/dist/src/metadata/test/template.d.ts +0 -1
  55. package/dist/src/metadata/test/template.d.ts.map +1 -1
  56. package/dist/src/metadata/test/template.js +0 -127
  57. package/dist/src/metadata/test/template.js.map +1 -1
  58. package/dist/src/modular/build-classical-client.d.ts.map +1 -1
  59. package/dist/src/modular/build-classical-client.js +2 -2
  60. package/dist/src/modular/build-classical-client.js.map +1 -1
  61. package/dist/src/modular/build-root-index.d.ts.map +1 -1
  62. package/dist/src/modular/build-root-index.js +37 -9
  63. package/dist/src/modular/build-root-index.js.map +1 -1
  64. package/dist/src/modular/build-subpath-index.d.ts.map +1 -1
  65. package/dist/src/modular/build-subpath-index.js +15 -2
  66. package/dist/src/modular/build-subpath-index.js.map +1 -1
  67. package/dist/src/modular/emit-models.d.ts.map +1 -1
  68. package/dist/src/modular/emit-models.js +24 -17
  69. package/dist/src/modular/emit-models.js.map +1 -1
  70. package/dist/src/modular/helpers/client-helpers.d.ts +8 -3
  71. package/dist/src/modular/helpers/client-helpers.d.ts.map +1 -1
  72. package/dist/src/modular/helpers/client-helpers.js +37 -17
  73. package/dist/src/modular/helpers/client-helpers.js.map +1 -1
  74. package/dist/src/modular/helpers/operation-helpers.js +8 -9
  75. package/dist/src/modular/helpers/operation-helpers.js.map +1 -1
  76. package/dist/src/transform/transform-client-options.d.ts.map +1 -1
  77. package/dist/src/transform/transform-client-options.js +1 -5
  78. package/dist/src/transform/transform-client-options.js.map +1 -1
  79. package/dist/tsconfig.tsbuildinfo +1 -1
  80. package/package.json +30 -33
  81. package/src/context-manager.ts +23 -0
  82. package/src/framework/declaration.ts +8 -20
  83. package/src/framework/hooks/sdk-types.ts +14 -0
  84. package/src/framework/source-file-batch.ts +140 -0
  85. package/src/index.ts +13 -13
  86. package/src/interfaces.ts +0 -2
  87. package/src/lib.ts +40 -14
  88. package/src/metadata/build-package-file.ts +2 -21
  89. package/src/metadata/build-readme-file.ts +20 -94
  90. package/src/metadata/build-ts-config.ts +12 -0
  91. package/src/metadata/package-json/build-azure-monorepo-package.ts +0 -17
  92. package/src/metadata/render-template.ts +202 -0
  93. package/src/metadata/test/build-recorded-client.ts +1 -7
  94. package/src/metadata/test/build-sample-test.ts +1 -4
  95. package/src/metadata/test/build-snippets.ts +2 -4
  96. package/src/metadata/test/template.ts +0 -128
  97. package/src/modular/build-classical-client.ts +2 -6
  98. package/src/modular/build-root-index.ts +52 -14
  99. package/src/modular/build-subpath-index.ts +24 -3
  100. package/src/modular/emit-models.ts +24 -18
  101. package/src/modular/helpers/client-helpers.ts +43 -25
  102. package/src/modular/helpers/operation-helpers.ts +10 -10
  103. package/src/transform/transform-client-options.ts +1 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azure-tools/typespec-ts",
3
- "version": "0.56.0-dev.3",
3
+ "version": "0.56.0-dev.4",
4
4
  "description": "A TypeSpec emitter for TypeScript",
5
5
  "main": "dist/src/index.js",
6
6
  "type": "module",
@@ -18,15 +18,15 @@
18
18
  "license": "MIT",
19
19
  "devDependencies": {
20
20
  "@azure-rest/core-client": "^2.3.1",
21
- "@typespec/http-specs": "^0.1.0-alpha.38 || >=0.1.0-alpha.39-dev <0.1.0-alpha.39",
22
- "@typespec/spector": "^0.1.0-alpha.25 || >=0.1.0-alpha.26-dev <0.1.0-alpha.26",
23
- "@typespec/spec-api": "^0.1.0-alpha.14 || >=0.1.0-alpha.15-dev <0.1.0-alpha.15",
24
- "@typespec/tspd": "^0.75.0 || >=0.76.0-dev <0.76.0",
25
- "@azure-tools/azure-http-specs": "^0.1.0-alpha.42 || >=0.1.0-alpha.43-dev <0.1.0-alpha.43",
26
- "@azure-tools/typespec-autorest": "^0.69.1 || >=0.70.0-dev <0.70.0",
27
- "@azure-tools/typespec-azure-core": "^0.69.0 || >=0.70.0-dev <0.70.0",
28
- "@azure-tools/typespec-azure-resource-manager": "^0.69.1 || >=0.70.0-dev <0.70.0",
29
- "@azure-tools/typespec-client-generator-core": "^0.69.1 || >=0.70.0-dev <0.70.0",
21
+ "@typespec/http-specs": "^0.1.0-alpha.39 || >=0.1.0-alpha.40-dev <0.1.0-alpha.40",
22
+ "@typespec/spector": "^0.1.0-alpha.26 || >=0.1.0-alpha.27-dev <0.1.0-alpha.27",
23
+ "@typespec/spec-api": "^0.1.0-alpha.15 || >=0.1.0-alpha.16-dev <0.1.0-alpha.16",
24
+ "@typespec/tspd": "^0.76.0 || >=0.77.0-dev <0.77.0",
25
+ "@azure-tools/azure-http-specs": "^0.1.0-alpha.43 || >=0.1.0-alpha.44-dev <0.1.0-alpha.44",
26
+ "@azure-tools/typespec-autorest": "^0.70.0 || >=0.71.0-dev <0.71.0",
27
+ "@azure-tools/typespec-azure-core": "^0.70.0 || >=0.71.0-dev <0.71.0",
28
+ "@azure-tools/typespec-azure-resource-manager": "^0.70.0 || >=0.71.0-dev <0.71.0",
29
+ "@azure-tools/typespec-client-generator-core": "^0.70.0 || >=0.71.0-dev <0.71.0",
30
30
  "@azure/abort-controller": "^2.1.2",
31
31
  "@azure/core-auth": "^1.6.0",
32
32
  "@azure/core-lro": "^3.1.0",
@@ -34,17 +34,15 @@
34
34
  "@azure/core-rest-pipeline": "^1.14.0",
35
35
  "@azure/core-util": "^1.4.0",
36
36
  "@azure/logger": "^1.0.4",
37
- "@typespec/compiler": "^1.13.0",
38
- "@typespec/http": "^1.13.0",
39
- "@typespec/openapi": "^1.13.0",
40
- "@typespec/rest": "^0.83.0 || >=0.84.0-dev <0.84.0",
41
- "@typespec/versioning": "^0.83.0 || >=0.84.0-dev <0.84.0",
42
- "@typespec/xml": "^0.83.0 || >=0.84.0-dev <0.84.0",
37
+ "@typespec/compiler": "^1.14.0",
38
+ "@typespec/http": "^1.14.0",
39
+ "@typespec/openapi": "^1.14.0",
40
+ "@typespec/rest": "^0.84.0 || >=0.85.0-dev <0.85.0",
41
+ "@typespec/versioning": "^0.84.0 || >=0.85.0-dev <0.85.0",
42
+ "@typespec/xml": "^0.84.0 || >=0.85.0-dev <0.85.0",
43
43
  "@typespec/ts-http-runtime": "0.3.6",
44
44
  "cross-env": "^10.1.0",
45
- "mkdirp": "^3.0.1",
46
- "npm-run-all": "~4.1.5",
47
- "prettier": "^3.8.1",
45
+ "concurrently": "^10.0.0",
48
46
  "vitest": "^4.1.3",
49
47
  "vite": "^8.0.8",
50
48
  "@vitest/coverage-v8": "^4.1.3",
@@ -52,17 +50,16 @@
52
50
  "yaml": "^2.8.3"
53
51
  },
54
52
  "peerDependencies": {
55
- "@azure-tools/typespec-azure-core": "^0.69.0 || >=0.70.0-dev <0.70.0",
56
- "@azure-tools/typespec-client-generator-core": "^0.69.1 || >=0.70.0-dev <0.70.0",
57
- "@typespec/compiler": "^1.13.0",
58
- "@typespec/http": "^1.13.0",
59
- "@typespec/rest": "^0.83.0 || >=0.84.0-dev <0.84.0",
60
- "@typespec/versioning": "^0.83.0 || >=0.84.0-dev <0.84.0",
61
- "@typespec/xml": "^0.83.0 || >=0.84.0-dev <0.84.0"
53
+ "@azure-tools/typespec-azure-core": "^0.70.0 || >=0.71.0-dev <0.71.0",
54
+ "@azure-tools/typespec-client-generator-core": "^0.70.0 || >=0.71.0-dev <0.71.0",
55
+ "@typespec/compiler": "^1.14.0",
56
+ "@typespec/http": "^1.14.0",
57
+ "@typespec/rest": "^0.84.0 || >=0.85.0-dev <0.85.0",
58
+ "@typespec/versioning": "^0.84.0 || >=0.85.0-dev <0.85.0",
59
+ "@typespec/xml": "^0.84.0 || >=0.85.0-dev <0.85.0"
62
60
  },
63
61
  "dependencies": {
64
62
  "fast-xml-parser": "^5.7.0",
65
- "handlebars": "^4.7.7",
66
63
  "prettier": "^3.8.1",
67
64
  "ts-morph": "^23.0.0",
68
65
  "tslib": "^2.3.1"
@@ -89,14 +86,14 @@
89
86
  "clean": "rimraf ./dist ./typespec-output",
90
87
  "build": "tsc -p .",
91
88
  "test": "npm run test-next && npm run unit-test && npm run integration-test-ci",
92
- "lint": "eslint . --ext .ts --max-warnings=0",
93
- "lint:fix": "eslint . --fix --ext .ts",
89
+ "lint": "oxlint . --deny-warnings",
90
+ "lint:fix": "oxlint . --fix",
94
91
  "check:tree": "node ./test/commands/check-clean-tree.ts",
95
- "integration-test-ci": "npm-run-all copy:typespec spector-test",
96
- "spector-test": "npm-run-all --silent -p start-test-server --race generate-and-run",
97
- "start-test-server": "mkdirp -p coverage && npx tsp-spector serve ./node_modules/@azure-tools/azure-http-specs/specs ./node_modules/@typespec/http-specs/specs --port 3002 --coverageFile ./coverage/spector-coverage-typescript-azure.json",
92
+ "integration-test-ci": "npm run copy:typespec && npm run spector-test",
93
+ "spector-test": "concurrently --kill-others --success first \"npm:start-test-server\" \"npm:generate-and-run\"",
94
+ "start-test-server": "node ./test/commands/start-test-server.js",
98
95
  "copy:typespec": "node ./test/commands/copy-typespec.ts",
99
- "generate-and-run": "npm run generate-tsp-only:client && npm-run-all -p integration-test:alone generate-tsp-only:declarations && npm run stop-test-server -- -p 3002",
96
+ "generate-and-run": "npm run generate-tsp-only:client && concurrently --kill-others-on-fail \"npm:integration-test:alone\" \"npm:generate-tsp-only:declarations\" && npm run stop-test-server -- -p 3002",
100
97
  "generate-tsp-only": "node ./test/commands/gen-spector.js",
101
98
  "generate-tsp-only:client": "node ./test/commands/gen-spector.js --phase=client",
102
99
  "generate-tsp-only:declarations": "node ./test/commands/gen-spector.js --phase=declarations",
@@ -72,6 +72,19 @@ class ContextManager {
72
72
  public getContext<K extends ContextKey>(key: K): Contexts[K] | undefined {
73
73
  return this.contexts.get(key) as Contexts[K] | undefined;
74
74
  }
75
+
76
+ /**
77
+ * Clears all stored contexts.
78
+ *
79
+ * The manager is a process-wide singleton, so the values provided during an
80
+ * emit (the `EmitContext`/`Program`, the TCGC `SdkContext`, the ts-morph
81
+ * `Project`, etc.) stay reachable from this map until the next emit overwrites
82
+ * them. Clearing at the end of an emit lets the whole previous program graph
83
+ * be collected instead of being retained until the following emit.
84
+ */
85
+ public clearContexts(): void {
86
+ this.contexts.clear();
87
+ }
75
88
  }
76
89
 
77
90
  // Expose the singleton instance of the context manager.
@@ -99,3 +112,13 @@ export function useContext<K extends ContextKey>(key: K): Contexts[K] {
99
112
  export function provideContext<K extends ContextKey>(key: K, value: Contexts[K]): void {
100
113
  contextManager.setContext(key, value);
101
114
  }
115
+
116
+ /**
117
+ * Clears all contexts held by the singleton context manager. Call this once an
118
+ * emit has finished writing its output so the retained program graph (compiler
119
+ * `EmitContext`/`Program`, TCGC `SdkContext`, ts-morph `Project`, binder, …) can
120
+ * be garbage collected instead of lingering until the next emit overwrites it.
121
+ */
122
+ export function clearContexts(): void {
123
+ contextManager.clearContexts();
124
+ }
@@ -8,12 +8,14 @@ import {
8
8
  InterfaceDeclaration,
9
9
  InterfaceDeclarationStructure,
10
10
  SourceFile,
11
+ StatementStructures,
11
12
  StructureKind,
12
13
  TypeAliasDeclaration,
13
14
  TypeAliasDeclarationStructure,
14
15
  } from "ts-morph";
15
16
  import { useBinder } from "./hooks/binder.js";
16
17
  import { refkey as getRefKey } from "./refkey.js";
18
+ import { enqueueStatement } from "./source-file-batch.js";
17
19
  export type DeclarationStructures =
18
20
  | ClassDeclarationStructure
19
21
  | EnumDeclarationStructure
@@ -80,25 +82,11 @@ export function addDeclaration(
80
82
  // Update the declaration name to be unique
81
83
  const trackedDeclaration = { ...declaration, name: trackedDeclarationName };
82
84
 
83
- switch (trackedDeclaration.kind) {
84
- case StructureKind.Class:
85
- sourceFile.addClass(trackedDeclaration);
86
- break;
87
- case StructureKind.Enum:
88
- sourceFile.addEnum(trackedDeclaration);
89
- break;
90
- case StructureKind.Function:
91
- sourceFile.addFunction(trackedDeclaration);
92
- break;
93
- case StructureKind.Interface:
94
- sourceFile.addInterface(trackedDeclaration);
95
- break;
96
- case StructureKind.TypeAlias:
97
- if (trackedDeclaration.type) {
98
- sourceFile.addTypeAlias(trackedDeclaration);
99
- }
100
- break;
101
- default:
102
- throw new Error(`Unsupported declaration kind ${(trackedDeclaration as any).kind}`);
85
+ // Skip empty type aliases (they have no body to emit). Done before
86
+ // dispatching so behaviour is identical whether batching or not.
87
+ if (trackedDeclaration.kind === StructureKind.TypeAlias && !trackedDeclaration.type) {
88
+ return;
103
89
  }
90
+
91
+ enqueueStatement(sourceFile, trackedDeclaration as StatementStructures);
104
92
  }
@@ -29,6 +29,20 @@ export const flattenPropertyModelMap: Map<SdkModelPropertyType, SdkModelType> =
29
29
  */
30
30
  export const pagedModelsKeptPublic = new Set<SdkType>();
31
31
 
32
+ /**
33
+ * Releases the module-level state that accumulates while visiting a package's
34
+ * types. These collections are cleared at the start of every
35
+ * {@link visitPackageTypes} call, but because they live at module scope they
36
+ * otherwise keep the most recently emitted program's SDK types reachable until
37
+ * the next emit runs. Call this once an emit has fully finished so that state
38
+ * can be garbage collected.
39
+ */
40
+ export function resetSdkTypesState(): void {
41
+ emitQueue.clear();
42
+ flattenPropertyModelMap.clear();
43
+ pagedModelsKeptPublic.clear();
44
+ }
45
+
32
46
  export interface SdkTypeContext {
33
47
  operations: Map<Type, SdkServiceMethod<SdkHttpOperation>>;
34
48
  types: Map<Type, SdkType>;
@@ -0,0 +1,140 @@
1
+ import {
2
+ ExportDeclarationStructure,
3
+ SourceFile,
4
+ StatementStructures,
5
+ StructureKind,
6
+ } from "ts-morph";
7
+
8
+ /**
9
+ * Generic per-source-file batch for ts-morph `add*` mutations.
10
+ *
11
+ * Each individual `sourceFile.addInterface/addFunction/addExportDeclaration/...`
12
+ * call re-parses the entire source file in ts-morph. Calling them in a loop
13
+ * therefore grows as O(N × file_size). This module collects structures while
14
+ * a batch is open and, on flush, issues a single `addStatements` call per file,
15
+ * collapsing N re-parses into 1.
16
+ *
17
+ * The batch is reference-counted so nested begin/flush pairs compose safely.
18
+ * Statements flush in insertion order and produce byte-identical output.
19
+ */
20
+
21
+ let batchDepth = 0;
22
+ const pendingByFile = new Map<SourceFile, StatementStructures[]>();
23
+
24
+ /**
25
+ * Opens a batching scope. Subsequent `enqueueStatement` calls queue rather
26
+ * than mutate the AST until the matching `flushSourceFileBatch` is called.
27
+ */
28
+ export function beginSourceFileBatch(): void {
29
+ batchDepth++;
30
+ }
31
+
32
+ /**
33
+ * Closes the most recent batching scope. When the outermost scope closes,
34
+ * all pending structures are written to their source files in bulk.
35
+ */
36
+ export function flushSourceFileBatch(): void {
37
+ if (batchDepth === 0) {
38
+ return;
39
+ }
40
+ batchDepth--;
41
+ if (batchDepth > 0) {
42
+ return;
43
+ }
44
+ try {
45
+ for (const [sourceFile, statements] of pendingByFile) {
46
+ if (statements.length === 0) {
47
+ continue;
48
+ }
49
+ sourceFile.addStatements(statements);
50
+ }
51
+ } finally {
52
+ pendingByFile.clear();
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Queues a statement structure for bulk-add on flush, or writes it
58
+ * immediately when no batch is open.
59
+ * @param sourceFile - Target source file.
60
+ * @param structure - The statement structure to add.
61
+ */
62
+ export function enqueueStatement(sourceFile: SourceFile, structure: StatementStructures): void {
63
+ if (batchDepth > 0) {
64
+ let pending = pendingByFile.get(sourceFile);
65
+ if (!pending) {
66
+ pending = [];
67
+ pendingByFile.set(sourceFile, pending);
68
+ }
69
+ pending.push(structure);
70
+ return;
71
+ }
72
+ sourceFile.addStatements([structure]);
73
+ }
74
+
75
+ /**
76
+ * Returns the set of names exported from `sourceFile` considering both the
77
+ * declarations already present in its AST and any export declarations queued
78
+ * by the current batch. Use this in place of reading `getExportDeclarations`
79
+ * directly when callers dedup against existing exports inside a batch.
80
+ * @param sourceFile - The file to inspect.
81
+ */
82
+ export function getEffectiveExportedNames(sourceFile: SourceFile): Set<string> {
83
+ const names = new Set<string>();
84
+ for (const decl of sourceFile.getExportDeclarations()) {
85
+ for (const named of decl.getNamedExports()) {
86
+ names.add(named.getAliasNode()?.getText() ?? named.getName());
87
+ }
88
+ }
89
+ const pending = pendingByFile.get(sourceFile);
90
+ if (pending) {
91
+ for (const structure of pending) {
92
+ if (structure.kind !== StructureKind.ExportDeclaration) {
93
+ continue;
94
+ }
95
+ collectNamedExportNames(structure.namedExports, names);
96
+ }
97
+ }
98
+ return names;
99
+ }
100
+
101
+ /**
102
+ * Returns the set of named exports queued in the current batch for
103
+ * `sourceFile`. Use this to union with any AST-based view of exports
104
+ * (e.g. `getExportedDeclarations`) when deduplicating against still-pending
105
+ * writes inside a batch.
106
+ * @param sourceFile - The file to inspect.
107
+ */
108
+ export function getQueuedExportNames(sourceFile: SourceFile): Set<string> {
109
+ const names = new Set<string>();
110
+ const pending = pendingByFile.get(sourceFile);
111
+ if (!pending) {
112
+ return names;
113
+ }
114
+ for (const structure of pending) {
115
+ if (structure.kind !== StructureKind.ExportDeclaration) {
116
+ continue;
117
+ }
118
+ collectNamedExportNames(structure.namedExports, names);
119
+ }
120
+ return names;
121
+ }
122
+
123
+ function collectNamedExportNames(
124
+ named: ExportDeclarationStructure["namedExports"],
125
+ into: Set<string>,
126
+ ): void {
127
+ if (!named || typeof named === "function") {
128
+ // WriterFunction form is opaque to us; callers can't dedup by name.
129
+ return;
130
+ }
131
+ for (const item of named) {
132
+ if (typeof item === "string") {
133
+ into.add(item);
134
+ } else if (typeof item === "function") {
135
+ continue;
136
+ } else if (item) {
137
+ into.add(item.alias ?? item.name);
138
+ }
139
+ }
140
+ }
package/src/index.ts CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  resolvePath,
11
11
  type CompilerHost,
12
12
  } from "@typespec/compiler";
13
- import { provideContext, useContext } from "./context-manager.js";
13
+ import { clearContexts, provideContext, useContext } from "./context-manager.js";
14
14
  import { buildRootIndex, buildSubClientIndexFile } from "./modular/build-root-index.js";
15
15
  import {
16
16
  AzureCoreDependencies,
@@ -43,7 +43,7 @@ import {
43
43
  } from "@azure-tools/typespec-client-generator-core";
44
44
  import { Project } from "ts-morph";
45
45
  import { provideBinder } from "./framework/hooks/binder.js";
46
- import { provideSdkTypes } from "./framework/hooks/sdk-types.js";
46
+ import { provideSdkTypes, resetSdkTypesState } from "./framework/hooks/sdk-types.js";
47
47
  import { loadStaticHelpers } from "./framework/load-static-helpers.js";
48
48
  import { ClientModel, ClientOptions } from "./interfaces.js";
49
49
  import { EmitterOptions } from "./lib.js";
@@ -76,7 +76,7 @@ import { buildSampleTest } from "./metadata/test/build-sample-test.js";
76
76
  import { buildSnippets } from "./metadata/test/build-snippets.js";
77
77
  import { buildClassicalClient } from "./modular/build-classical-client.js";
78
78
  import { buildClassicOperationFiles } from "./modular/build-classical-operation-groups.js";
79
- import { buildClientContext, getClientContextPath } from "./modular/build-client-context.js";
79
+ import { buildClientContext } from "./modular/build-client-context.js";
80
80
  import { transformModularEmitterOptions } from "./modular/build-modular-options.js";
81
81
  import { buildOperationFiles } from "./modular/build-operations.js";
82
82
  import { getModuleExports } from "./modular/build-project-files.js";
@@ -188,6 +188,15 @@ export async function $onEmit(context: EmitContext) {
188
188
 
189
189
  await generateMetadataAndTest(dpgContext);
190
190
 
191
+ // Release the emitter's process-wide singleton state now that this emit has
192
+ // finished writing output. Without this, the context manager keeps the whole
193
+ // previous program graph (compiler `EmitContext`/`Program`, TCGC `SdkContext`,
194
+ // ts-morph `Project`, binder, …) reachable until the next emit overwrites it.
195
+ // Clearing here lets it be collected between emits, which matters for hosts
196
+ // that run many emits in one process (test suites, benchmarks, watch mode).
197
+ clearContexts();
198
+ resetSdkTypesState();
199
+
191
200
  async function enrichDpgContext() {
192
201
  const generationPathDetail: GenerationDirDetail = await calculateGenerationDir();
193
202
  dpgContext.generationPathDetail = generationPathDetail;
@@ -446,7 +455,6 @@ export async function $onEmit(context: EmitContext) {
446
455
  modularPackageInfo = {
447
456
  ...modularPackageInfo,
448
457
  dependencies,
449
- clientContextPaths: getRelativeContextPaths(context, modularEmitterOptions),
450
458
  };
451
459
  }
452
460
  commonBuilders.push((model) => buildPackageFile(model, modularPackageInfo));
@@ -499,14 +507,13 @@ export async function $onEmit(context: EmitContext) {
499
507
  }
500
508
  const modularPackageInfo = {
501
509
  exports: getModuleExports(context, modularEmitterOptions),
502
- clientContextPaths: getRelativeContextPaths(context, modularEmitterOptions),
503
510
  ...(Object.keys(additionalDependencies).length > 0 && {
504
511
  dependencies: additionalDependencies,
505
512
  }),
506
513
  };
507
514
 
508
515
  // Always update package.json (adds #platform/* imports) along with
509
- // exports, clientContextPaths and LRO deps.
516
+ // exports and LRO deps.
510
517
  {
511
518
  // Read package.json content via host and pass parsed object
512
519
  const pkgSourceFile = await host.readFile(existingPackageFilePath);
@@ -571,13 +578,6 @@ export async function $onEmit(context: EmitContext) {
571
578
  );
572
579
  }
573
580
  }
574
-
575
- function getRelativeContextPaths(context: SdkContext, options: ModularEmitterOptions) {
576
- const clientMap = getClientHierarchyMap(context);
577
- return Array.from(clientMap)
578
- .map((subClient) => getClientContextPath(subClient, options))
579
- .map((path) => path.substring(path.indexOf("src")));
580
- }
581
581
  }
582
582
 
583
583
  export async function createContextWithDefaultOptions(
package/src/interfaces.ts CHANGED
@@ -223,7 +223,6 @@ export interface ClientOptions {
223
223
  generateTest?: boolean;
224
224
  generateSample?: boolean;
225
225
  azureOutputDirectory?: string;
226
- isTypeSpecTest?: boolean;
227
226
  serviceInfo?: ServiceInfo;
228
227
  azureArm?: boolean;
229
228
  enableOperationGroup?: boolean;
@@ -335,7 +334,6 @@ export interface PackageDetails {
335
334
  nameWithoutScope?: string;
336
335
  description?: string;
337
336
  version?: string;
338
- isVersionUserProvided?: boolean;
339
337
  }
340
338
  export interface OperationParameter {
341
339
  operationGroup: string;
package/src/lib.ts CHANGED
@@ -6,6 +6,21 @@ import { Options } from "prettier";
6
6
  import { PackageDetails } from "./interfaces.js";
7
7
 
8
8
  export interface EmitterOptions {
9
+ /**
10
+ * Use this flag if you would like to generate the sdk only for a specific version.
11
+ * Also accepts values `latest` and `all`, or a map from service namespace full name to version for multi-service packages.
12
+ */
13
+ "api-version"?: string | Record<string, string>;
14
+ /**
15
+ * Specifies the directory where the emitter will look for example files.
16
+ * Defaults to an `examples` directory located at the project root.
17
+ */
18
+ "examples-dir"?: string;
19
+ /**
20
+ * Specifies the namespace you want to override for namespaces set in the spec.
21
+ * With this config, all namespace for the spec types will default to it.
22
+ */
23
+ namespace?: string;
9
24
  /**
10
25
  * Indicates whether to include response headers in the generated response type for modular operations.
11
26
  * When set to true, modular operation responses with model or void bodies will have their headers
@@ -39,7 +54,6 @@ export interface EmitterOptions {
39
54
  */
40
55
  "generate-test"?: boolean;
41
56
  "generate-sample"?: boolean;
42
- "is-typespec-test"?: boolean;
43
57
  "azure-arm"?: boolean;
44
58
  "enable-operation-group"?: boolean;
45
59
  "enable-model-namespace"?: boolean;
@@ -51,8 +65,6 @@ export interface EmitterOptions {
51
65
  "ignore-property-name-normalize"?: boolean;
52
66
  "typespec-title-map"?: Record<string, string>;
53
67
  "ignore-enum-member-name-normalize"?: boolean;
54
- //TODO should remove this after finish the release tool test
55
- "should-use-pnpm-dep"?: boolean;
56
68
  "ignore-nullable-on-optional"?: boolean;
57
69
  /**
58
70
  * When set to true (default for Azure services), non-model return types (arrays, scalars, enums,
@@ -85,6 +97,31 @@ export const EmitterOptionsSchema: JSONSchemaType<EmitterOptions> = {
85
97
  type: "object",
86
98
  additionalProperties: true,
87
99
  properties: {
100
+ "api-version": {
101
+ oneOf: [
102
+ { type: "string", nullable: true },
103
+ {
104
+ type: "object",
105
+ additionalProperties: { type: "string" },
106
+ required: [],
107
+ nullable: true,
108
+ },
109
+ ],
110
+ description:
111
+ "Use this flag if you would like to generate the sdk only for a specific version. Default value is the latest version. Also accepts values `latest` and `all`. For multi-service packages, provide a map from each service namespace's full name to its desired version; services not listed default to their latest version.",
112
+ } as any,
113
+ "examples-dir": {
114
+ type: "string",
115
+ nullable: true,
116
+ description:
117
+ "Specifies the directory where the emitter will look for example files. If the flag isn't set, the emitter defaults to using an `examples` directory located at the project root.",
118
+ },
119
+ namespace: {
120
+ type: "string",
121
+ nullable: true,
122
+ description:
123
+ "Specifies the namespace you want to override for namespaces set in the spec. With this config, all namespace for the spec types will default to it.",
124
+ },
88
125
  "include-headers-in-response": {
89
126
  type: "boolean",
90
127
  nullable: true,
@@ -100,7 +137,6 @@ export const EmitterOptionsSchema: JSONSchemaType<EmitterOptions> = {
100
137
  nameWithoutScope: { type: "string", nullable: true },
101
138
  description: { type: "string", nullable: true },
102
139
  version: { type: "string", nullable: true },
103
- isVersionUserProvided: { type: "boolean", nullable: true },
104
140
  },
105
141
  required: ["name"],
106
142
  nullable: true,
@@ -170,11 +206,6 @@ export const EmitterOptionsSchema: JSONSchemaType<EmitterOptions> = {
170
206
  description:
171
207
  "Whether to generate sample files, for basic samples of your generated sdks. Defaults to `undefined`. Management packages' default to `true`.",
172
208
  },
173
- "is-typespec-test": {
174
- type: "boolean",
175
- nullable: true,
176
- description: "Internal option for test",
177
- },
178
209
  "azure-arm": {
179
210
  type: "boolean",
180
211
  nullable: true,
@@ -251,11 +282,6 @@ export const EmitterOptionsSchema: JSONSchemaType<EmitterOptions> = {
251
282
  "```",
252
283
  ].join("\n"),
253
284
  },
254
- "should-use-pnpm-dep": {
255
- type: "boolean",
256
- nullable: true,
257
- description: "Internal option for test.",
258
- },
259
285
  "ignore-nullable-on-optional": {
260
286
  type: "boolean",
261
287
  nullable: true,
@@ -11,12 +11,11 @@ import { getPackageName } from "./utils.js";
11
11
  interface PackageFileOptions {
12
12
  exports?: Record<string, any>;
13
13
  dependencies?: Record<string, string>;
14
- clientContextPaths?: string[];
15
14
  }
16
15
 
17
16
  export function buildPackageFile(
18
17
  model: ClientModel,
19
- { exports, dependencies, clientContextPaths }: PackageFileOptions = {},
18
+ { exports, dependencies }: PackageFileOptions = {},
20
19
  ) {
21
20
  const config: PackageCommonInfoConfig = {
22
21
  description: getDescription(model),
@@ -35,7 +34,6 @@ export function buildPackageFile(
35
34
  hasLro: hasPollingOperations(model),
36
35
  monorepoPackageDirectory: model.options?.azureOutputDirectory,
37
36
  dependencies,
38
- clientContextPaths,
39
37
  };
40
38
 
41
39
  const packageInfo: Record<string, any> = buildAzureMonorepoPackage(extendedConfig);
@@ -63,17 +61,15 @@ export function buildPackageFile(
63
61
  * - Adds LRO dependencies (`@azure/core-lro`, `@azure/abort-controller`) when the package has
64
62
  * polling operations (for non-monorepo Azure packages).
65
63
  * - Updates exports (tshy or warp) when `exports` is provided.
66
- * - Updates `//metadata.constantPaths` when `clientContextPaths` is provided.
67
64
  */
68
65
  export function updatePackageFile(
69
66
  model: ClientModel,
70
67
  existingFilePathOrContent: string | Record<string, any>,
71
- { exports, clientContextPaths }: PackageFileOptions = {},
68
+ { exports }: PackageFileOptions = {},
72
69
  ) {
73
70
  const hasLro = hasPollingOperations(model);
74
71
  const needsLroUpdate = hasLro;
75
72
  const needsExportsUpdate = exports;
76
- const needsConstantPathsUpdate = clientContextPaths && clientContextPaths.length > 0;
77
73
 
78
74
  let packageInfo;
79
75
  if (typeof existingFilePathOrContent === "string") {
@@ -146,21 +142,6 @@ export function updatePackageFile(
146
142
  };
147
143
  }
148
144
 
149
- // Update constantPaths metadata for Azure packages
150
- if (needsConstantPathsUpdate && packageInfo["//metadata"]) {
151
- const metadata = packageInfo["//metadata"];
152
- // Filter out existing userAgentInfo entries
153
- const nonUserAgentPaths = (metadata.constantPaths || []).filter(
154
- (item: any) => item.prefix !== "userAgentInfo",
155
- );
156
- // Add new userAgentInfo entries from clientContextPaths
157
- const newUserAgentPaths = clientContextPaths!.map((path) => ({
158
- path: path,
159
- prefix: "userAgentInfo",
160
- }));
161
- metadata.constantPaths = [...nonUserAgentPaths, ...newUserAgentPaths];
162
- }
163
-
164
145
  return {
165
146
  path: "package.json",
166
147
  content: JSON.stringify(packageInfo, null, 2),