@azure-tools/typespec-ts 0.55.0-dev.3 → 0.55.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.
- package/dist/src/framework/load-static-helpers.d.ts +4 -1
- package/dist/src/framework/load-static-helpers.d.ts.map +1 -1
- package/dist/src/framework/load-static-helpers.js +15 -10
- package/dist/src/framework/load-static-helpers.js.map +1 -1
- package/dist/src/framework/sample.js +1 -1
- package/dist/src/framework/sample.js.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +37 -18
- package/dist/src/index.js.map +1 -1
- package/dist/src/modular/emit-tests.d.ts +2 -1
- package/dist/src/modular/emit-tests.d.ts.map +1 -1
- package/dist/src/modular/emit-tests.js +18 -8
- package/dist/src/modular/emit-tests.js.map +1 -1
- package/dist/src/rlc-common/build-client-definitions.js +1 -1
- package/dist/src/rlc-common/build-client-definitions.js.map +1 -1
- package/dist/src/rlc-common/build-client.js +1 -1
- package/dist/src/rlc-common/build-client.js.map +1 -1
- package/dist/src/rlc-common/build-index-file.js +1 -1
- package/dist/src/rlc-common/build-index-file.js.map +1 -1
- package/dist/src/rlc-common/build-is-unexpected-helper.js +1 -1
- package/dist/src/rlc-common/build-is-unexpected-helper.js.map +1 -1
- package/dist/src/rlc-common/build-logger.js +1 -1
- package/dist/src/rlc-common/build-logger.js.map +1 -1
- package/dist/src/rlc-common/build-parameter-types.js +1 -1
- package/dist/src/rlc-common/build-parameter-types.js.map +1 -1
- package/dist/src/rlc-common/build-response-types.js +1 -1
- package/dist/src/rlc-common/build-response-types.js.map +1 -1
- package/dist/src/rlc-common/build-schema-type.js +1 -1
- package/dist/src/rlc-common/build-schema-type.js.map +1 -1
- package/dist/src/rlc-common/build-top-level-index-file.js +1 -1
- package/dist/src/rlc-common/build-top-level-index-file.js.map +1 -1
- package/dist/src/rlc-common/metadata/build-api-extractor-config.js +1 -1
- package/dist/src/rlc-common/metadata/build-api-extractor-config.js.map +1 -1
- package/dist/src/rlc-common/metadata/build-es-lint-config.js +1 -1
- package/dist/src/rlc-common/metadata/build-es-lint-config.js.map +1 -1
- package/dist/src/rlc-common/metadata/build-package-file.js +1 -1
- package/dist/src/rlc-common/metadata/build-package-file.js.map +1 -1
- package/dist/src/rlc-common/metadata/build-readme-file.d.ts +2 -2
- package/dist/src/rlc-common/metadata/build-readme-file.d.ts.map +1 -1
- package/dist/src/rlc-common/metadata/build-readme-file.js +5 -8
- package/dist/src/rlc-common/metadata/build-readme-file.js.map +1 -1
- package/dist/src/rlc-common/metadata/build-rollup-config.js +1 -1
- package/dist/src/rlc-common/metadata/build-rollup-config.js.map +1 -1
- package/dist/src/rlc-common/metadata/build-ts-config.js +1 -1
- package/dist/src/rlc-common/metadata/build-ts-config.js.map +1 -1
- package/dist/src/utils/emit-util.d.ts.map +1 -1
- package/dist/src/utils/emit-util.js +7 -1
- package/dist/src/utils/emit-util.js.map +1 -1
- package/dist/src/utils/file-system-utils.d.ts +4 -4
- package/dist/src/utils/file-system-utils.d.ts.map +1 -1
- package/dist/src/utils/file-system-utils.js +12 -13
- package/dist/src/utils/file-system-utils.js.map +1 -1
- package/dist/src/utils/resolve-project-root.d.ts +6 -4
- package/dist/src/utils/resolve-project-root.d.ts.map +1 -1
- package/dist/src/utils/resolve-project-root.js +11 -16
- package/dist/src/utils/resolve-project-root.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/framework/load-static-helpers.ts +25 -13
- package/src/framework/sample.ts +1 -1
- package/src/index.ts +44 -16
- package/src/modular/emit-tests.ts +22 -9
- package/src/rlc-common/build-client-definitions.ts +1 -1
- package/src/rlc-common/build-client.ts +1 -1
- package/src/rlc-common/build-index-file.ts +1 -1
- package/src/rlc-common/build-is-unexpected-helper.ts +1 -1
- package/src/rlc-common/build-logger.ts +1 -1
- package/src/rlc-common/build-parameter-types.ts +1 -1
- package/src/rlc-common/build-response-types.ts +1 -1
- package/src/rlc-common/build-schema-type.ts +1 -1
- package/src/rlc-common/build-top-level-index-file.ts +1 -1
- package/src/rlc-common/metadata/build-api-extractor-config.ts +1 -1
- package/src/rlc-common/metadata/build-es-lint-config.ts +1 -1
- package/src/rlc-common/metadata/build-package-file.ts +1 -1
- package/src/rlc-common/metadata/build-readme-file.ts +5 -8
- package/src/rlc-common/metadata/build-rollup-config.ts +1 -1
- package/src/rlc-common/metadata/build-ts-config.ts +1 -1
- package/src/utils/emit-util.ts +7 -4
- package/src/utils/file-system-utils.ts +13 -13
- package/src/utils/resolve-project-root.ts +11 -22
package/package.json
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { joinPaths, NoTarget, Program } from "@typespec/compiler";
|
|
2
|
-
import { readdir, readFile, stat } from "fs/promises";
|
|
1
|
+
import { type CompilerHost, joinPaths, NoTarget, Program } from "@typespec/compiler";
|
|
3
2
|
import {
|
|
4
3
|
ClassDeclaration,
|
|
5
4
|
EnumDeclaration,
|
|
@@ -38,6 +37,9 @@ export interface LoadStaticHelpersOptions extends Partial<ModularEmitterOptions>
|
|
|
38
37
|
sourcesDir?: string;
|
|
39
38
|
rootDir?: string;
|
|
40
39
|
program?: Program;
|
|
40
|
+
host?: CompilerHost;
|
|
41
|
+
/** The emitter package root directory (where static/ lives). */
|
|
42
|
+
packageRoot?: string;
|
|
41
43
|
/** When true, also load test helpers from static/test-helpers/ into test/generated/util/ */
|
|
42
44
|
loadTestHelpers?: boolean;
|
|
43
45
|
}
|
|
@@ -53,13 +55,20 @@ export async function loadStaticHelpers(
|
|
|
53
55
|
options: LoadStaticHelpersOptions = {},
|
|
54
56
|
): Promise<Map<string, StaticHelperMetadata>> {
|
|
55
57
|
const helpersMap = new Map<string, StaticHelperMetadata>();
|
|
58
|
+
const host = options.host ?? options.program?.host;
|
|
59
|
+
if (!host) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
"loadStaticHelpers requires either a host or program in options to access the file system.",
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const packageRoot = options.packageRoot ?? resolveProjectRoot();
|
|
66
|
+
|
|
56
67
|
// Load static helpers used in sources code
|
|
57
|
-
const defaultStaticHelpersPath = joinPaths(
|
|
58
|
-
resolveProjectRoot(),
|
|
59
|
-
DEFAULT_SOURCES_STATIC_HELPERS_PATH,
|
|
60
|
-
);
|
|
68
|
+
const defaultStaticHelpersPath = joinPaths(packageRoot, DEFAULT_SOURCES_STATIC_HELPERS_PATH);
|
|
61
69
|
const filesInSources = await traverseDirectory(
|
|
62
70
|
options.helpersAssetDirectory ?? defaultStaticHelpersPath,
|
|
71
|
+
host,
|
|
63
72
|
options.program,
|
|
64
73
|
);
|
|
65
74
|
await loadFiles(filesInSources, options.sourcesDir ?? "");
|
|
@@ -68,12 +77,10 @@ export async function loadStaticHelpers(
|
|
|
68
77
|
options.loadTestHelpers ??
|
|
69
78
|
(options.options?.generateTest && isAzurePackage({ options: options.options }))
|
|
70
79
|
) {
|
|
71
|
-
const defaultTestingHelpersPath = joinPaths(
|
|
72
|
-
resolveProjectRoot(),
|
|
73
|
-
DEFAULT_SOURCES_TESTING_HELPERS_PATH,
|
|
74
|
-
);
|
|
80
|
+
const defaultTestingHelpersPath = joinPaths(packageRoot, DEFAULT_SOURCES_TESTING_HELPERS_PATH);
|
|
75
81
|
const filesInTestings = await traverseDirectory(
|
|
76
82
|
defaultTestingHelpersPath,
|
|
83
|
+
host,
|
|
77
84
|
options.program,
|
|
78
85
|
[],
|
|
79
86
|
"",
|
|
@@ -86,7 +93,8 @@ export async function loadStaticHelpers(
|
|
|
86
93
|
async function loadFiles(files: FileMetadata[], generateDir: string) {
|
|
87
94
|
for (const file of files) {
|
|
88
95
|
const targetPath = joinPaths(generateDir, file.target);
|
|
89
|
-
const
|
|
96
|
+
const sourceFile = await host!.readFile(file.source);
|
|
97
|
+
const contents = sourceFile.text;
|
|
90
98
|
const addedFile = project.createSourceFile(targetPath, contents, {
|
|
91
99
|
overwrite: true,
|
|
92
100
|
});
|
|
@@ -186,25 +194,29 @@ function getDeclarationByMetadata(
|
|
|
186
194
|
}
|
|
187
195
|
}
|
|
188
196
|
|
|
197
|
+
type FsHost = Pick<CompilerHost, "readFile" | "readDir" | "stat">;
|
|
198
|
+
|
|
189
199
|
const _targetStaticHelpersBaseDir = "static-helpers";
|
|
190
200
|
async function traverseDirectory(
|
|
191
201
|
directory: string,
|
|
202
|
+
host: FsHost,
|
|
192
203
|
program?: Program,
|
|
193
204
|
result: { source: string; target: string }[] = [],
|
|
194
205
|
relativePath: string = "",
|
|
195
206
|
targetBaseDir: string = _targetStaticHelpersBaseDir,
|
|
196
207
|
): Promise<{ source: string; target: string }[]> {
|
|
197
208
|
try {
|
|
198
|
-
const files = await
|
|
209
|
+
const files = await host.readDir(directory);
|
|
199
210
|
|
|
200
211
|
await Promise.all(
|
|
201
212
|
files.map(async (file) => {
|
|
202
213
|
const filePath = joinPaths(directory, file);
|
|
203
|
-
const fileStat = await stat(filePath);
|
|
214
|
+
const fileStat = await host.stat(filePath);
|
|
204
215
|
|
|
205
216
|
if (fileStat.isDirectory()) {
|
|
206
217
|
await traverseDirectory(
|
|
207
218
|
filePath,
|
|
219
|
+
host,
|
|
208
220
|
program,
|
|
209
221
|
result,
|
|
210
222
|
joinPaths(relativePath, file),
|
package/src/framework/sample.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { useBinder } from "./hooks/binder.js";
|
|
|
9
9
|
import { resolveReference } from "./reference.js";
|
|
10
10
|
|
|
11
11
|
// Create a new ts-morph project
|
|
12
|
-
const project = new Project();
|
|
12
|
+
const project = new Project({ useInMemoryFileSystem: true });
|
|
13
13
|
|
|
14
14
|
// Create a source file
|
|
15
15
|
const sourceFile = project.createSourceFile("test.ts", "", { overwrite: true });
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import {
|
|
5
|
+
EmitContext,
|
|
6
|
+
Program,
|
|
7
|
+
getBaseFileName,
|
|
8
|
+
getDirectoryPath,
|
|
9
|
+
joinPaths,
|
|
10
|
+
resolvePath,
|
|
11
|
+
type CompilerHost,
|
|
12
|
+
} from "@typespec/compiler";
|
|
6
13
|
import { provideContext, useContext } from "./context-manager.js";
|
|
7
14
|
import { buildRootIndex, buildSubClientIndexFile } from "./modular/build-root-index.js";
|
|
8
15
|
import {
|
|
@@ -117,8 +124,15 @@ export async function $onEmit(context: EmitContext) {
|
|
|
117
124
|
return;
|
|
118
125
|
}
|
|
119
126
|
/** Shared status */
|
|
120
|
-
const outputProject = new Project();
|
|
127
|
+
const outputProject = new Project({ useInMemoryFileSystem: true });
|
|
121
128
|
const program: Program = context.program;
|
|
129
|
+
const host: CompilerHost = program.host;
|
|
130
|
+
// Derive the emitter package root from the compiler's resolved emitter entry point.
|
|
131
|
+
// This works correctly in both Node.js and the browser playground VFS.
|
|
132
|
+
const emitterRef = program.emitters.find((e) => e.metadata.name === "@azure-tools/typespec-ts");
|
|
133
|
+
const emitterPackageRoot = emitterRef
|
|
134
|
+
? resolvePath(getDirectoryPath(emitterRef.main), "../..")
|
|
135
|
+
: undefined;
|
|
122
136
|
const emitterOptions: EmitterOptions = context.options;
|
|
123
137
|
const dpgContext = await createContextWithDefaultOptions(context);
|
|
124
138
|
|
|
@@ -160,6 +174,8 @@ export async function $onEmit(context: EmitContext) {
|
|
|
160
174
|
rootDir: dpgContext.generationPathDetail?.rootDir,
|
|
161
175
|
options: rlcOptions,
|
|
162
176
|
program,
|
|
177
|
+
host,
|
|
178
|
+
packageRoot: emitterPackageRoot,
|
|
163
179
|
},
|
|
164
180
|
);
|
|
165
181
|
const extraDependencies = isAzurePackage({ options: rlcOptions })
|
|
@@ -216,9 +232,10 @@ export async function $onEmit(context: EmitContext) {
|
|
|
216
232
|
// clear output folder if needed
|
|
217
233
|
if (options.clearOutputFolder) {
|
|
218
234
|
// Clear output directory while preserving TempTypeSpecFiles
|
|
219
|
-
await clearDirectory(context.emitterOutputDir, ["TempTypeSpecFiles"], program);
|
|
235
|
+
await clearDirectory(host, context.emitterOutputDir, ["TempTypeSpecFiles"], program);
|
|
220
236
|
}
|
|
221
237
|
const hasTestFolder = await pathExists(
|
|
238
|
+
host,
|
|
222
239
|
joinPaths(dpgContext.generationPathDetail?.metadataDir ?? "", "test"),
|
|
223
240
|
);
|
|
224
241
|
options.generateTest =
|
|
@@ -234,10 +251,10 @@ export async function $onEmit(context: EmitContext) {
|
|
|
234
251
|
const customizationFolder = joinPaths(projectRoot, "generated");
|
|
235
252
|
const srcGeneratedFolder = joinPaths(projectRoot, "src", "generated");
|
|
236
253
|
// if customization folder exists, use it as sources root
|
|
237
|
-
const finalCustomizationFolder = (await pathExists(srcGeneratedFolder))
|
|
254
|
+
const finalCustomizationFolder = (await pathExists(host, srcGeneratedFolder))
|
|
238
255
|
? srcGeneratedFolder
|
|
239
256
|
: customizationFolder;
|
|
240
|
-
const sourcesRoot = (await pathExists(finalCustomizationFolder))
|
|
257
|
+
const sourcesRoot = (await pathExists(host, finalCustomizationFolder))
|
|
241
258
|
? finalCustomizationFolder
|
|
242
259
|
: joinPaths(projectRoot, "src");
|
|
243
260
|
return {
|
|
@@ -250,6 +267,7 @@ export async function $onEmit(context: EmitContext) {
|
|
|
250
267
|
|
|
251
268
|
async function clearSrcFolder() {
|
|
252
269
|
await emptyDir(
|
|
270
|
+
host,
|
|
253
271
|
dpgContext.generationPathDetail?.modularSourcesDir ??
|
|
254
272
|
dpgContext.generationPathDetail?.rlcSourcesDir ??
|
|
255
273
|
"",
|
|
@@ -262,8 +280,8 @@ export async function $onEmit(context: EmitContext) {
|
|
|
262
280
|
dpgContext.generationPathDetail?.rootDir ?? "",
|
|
263
281
|
"samples-dev",
|
|
264
282
|
);
|
|
265
|
-
if (await pathExists(samplesDevPath)) {
|
|
266
|
-
await emptyDir(samplesDevPath);
|
|
283
|
+
if (await pathExists(host, samplesDevPath)) {
|
|
284
|
+
await emptyDir(host, samplesDevPath);
|
|
267
285
|
}
|
|
268
286
|
}
|
|
269
287
|
}
|
|
@@ -459,23 +477,23 @@ export async function $onEmit(context: EmitContext) {
|
|
|
459
477
|
dpgContext.generationPathDetail?.metadataDir ?? "",
|
|
460
478
|
"package.json",
|
|
461
479
|
);
|
|
462
|
-
const hasPackageFile =
|
|
480
|
+
const hasPackageFile = await pathExists(host, existingPackageFilePath);
|
|
463
481
|
const existingReadmeFilePath = joinPaths(
|
|
464
482
|
dpgContext.generationPathDetail?.metadataDir ?? "",
|
|
465
483
|
"README.md",
|
|
466
484
|
);
|
|
467
|
-
const hasReadmeFile =
|
|
485
|
+
const hasReadmeFile = await pathExists(host, existingReadmeFilePath);
|
|
468
486
|
const existingChangelogFilePath = joinPaths(
|
|
469
487
|
dpgContext.generationPathDetail?.metadataDir ?? "",
|
|
470
488
|
"CHANGELOG.md",
|
|
471
489
|
);
|
|
472
|
-
const hasChangelogFile =
|
|
490
|
+
const hasChangelogFile = await pathExists(host, existingChangelogFilePath);
|
|
473
491
|
const shouldGenerateMetadata = option.generateMetadata === true || !hasPackageFile;
|
|
474
492
|
const existingTestFolderPath = joinPaths(
|
|
475
493
|
dpgContext.generationPathDetail?.metadataDir ?? "",
|
|
476
494
|
"test",
|
|
477
495
|
);
|
|
478
|
-
const hasTestFolder =
|
|
496
|
+
const hasTestFolder = await pathExists(host, existingTestFolderPath);
|
|
479
497
|
if (option.generateTest === undefined) {
|
|
480
498
|
if (hasTestFolder) {
|
|
481
499
|
option.generateTest = false;
|
|
@@ -516,7 +534,7 @@ export async function $onEmit(context: EmitContext) {
|
|
|
516
534
|
option.azureSdkForJs === true &&
|
|
517
535
|
emitterOptions["generate-metadata"] === true
|
|
518
536
|
) {
|
|
519
|
-
await emitTests(dpgContext);
|
|
537
|
+
await emitTests(dpgContext, host);
|
|
520
538
|
}
|
|
521
539
|
let modularPackageInfo = {};
|
|
522
540
|
if (option.isModularLibrary) {
|
|
@@ -617,8 +635,16 @@ export async function $onEmit(context: EmitContext) {
|
|
|
617
635
|
// Always update package.json for monorepo packages (adds #platform/* imports)
|
|
618
636
|
// and for modular packages (adds exports, clientContextPaths, LRO deps)
|
|
619
637
|
if (option.isModularLibrary || option.azureSdkForJs) {
|
|
638
|
+
// Read package.json content via host and pass parsed object
|
|
639
|
+
const pkgSourceFile = await host.readFile(existingPackageFilePath);
|
|
640
|
+
let packageInfo: Record<string, any>;
|
|
641
|
+
try {
|
|
642
|
+
packageInfo = JSON.parse(pkgSourceFile.text);
|
|
643
|
+
} catch {
|
|
644
|
+
packageInfo = {};
|
|
645
|
+
}
|
|
620
646
|
updateBuilders.push((model: RLCModel) =>
|
|
621
|
-
updatePackageFile(model,
|
|
647
|
+
updatePackageFile(model, packageInfo, modularPackageInfo),
|
|
622
648
|
);
|
|
623
649
|
}
|
|
624
650
|
|
|
@@ -630,11 +656,13 @@ export async function $onEmit(context: EmitContext) {
|
|
|
630
656
|
// If the client name changed, regenerate the README and snippets completely;
|
|
631
657
|
// otherwise update only the API reference link in-place.
|
|
632
658
|
if (hasReadmeFile) {
|
|
633
|
-
const
|
|
659
|
+
const readmeSourceFile = await host.readFile(existingReadmeFilePath);
|
|
660
|
+
const existingReadmeContent = readmeSourceFile.text;
|
|
661
|
+
const clientNameChanged = hasClientNameChanged(rlcClient, existingReadmeContent);
|
|
634
662
|
updateBuilders.push(
|
|
635
663
|
clientNameChanged
|
|
636
664
|
? buildReadmeFile
|
|
637
|
-
: (model: RLCModel) => updateReadmeFile(model,
|
|
665
|
+
: (model: RLCModel) => updateReadmeFile(model, existingReadmeContent),
|
|
638
666
|
);
|
|
639
667
|
|
|
640
668
|
// Regenerate snippets.spec.ts only when the client name changed
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { joinPaths } from "@typespec/compiler";
|
|
2
|
-
import { existsSync, rmSync } from "fs";
|
|
1
|
+
import { type CompilerHost, joinPaths } from "@typespec/compiler";
|
|
3
2
|
import { SourceFile } from "ts-morph";
|
|
4
3
|
import { resolveReference } from "../framework/reference.js";
|
|
5
4
|
import { NameType, normalizeName } from "../rlc-common/index.js";
|
|
@@ -22,7 +21,7 @@ import { CreateRecorderHelpers } from "./static-helpers-metadata.js";
|
|
|
22
21
|
/**
|
|
23
22
|
* Clean up the test/generated folder before generating new tests
|
|
24
23
|
*/
|
|
25
|
-
async function cleanupTestFolder(dpgContext: SdkContext) {
|
|
24
|
+
async function cleanupTestFolder(dpgContext: SdkContext, host: CompilerHost) {
|
|
26
25
|
const clients = dpgContext.sdkPackage.clients;
|
|
27
26
|
const baseTestFolder = joinPaths(
|
|
28
27
|
dpgContext.generationPathDetail?.rootDir ?? "",
|
|
@@ -30,19 +29,28 @@ async function cleanupTestFolder(dpgContext: SdkContext) {
|
|
|
30
29
|
"generated",
|
|
31
30
|
);
|
|
32
31
|
|
|
32
|
+
async function dirExists(path: string): Promise<boolean> {
|
|
33
|
+
try {
|
|
34
|
+
const s = await host.stat(path);
|
|
35
|
+
return s.isDirectory();
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
33
41
|
// If there are multiple clients, clean up subfolders
|
|
34
42
|
if (clients.length > 1) {
|
|
35
43
|
for (const client of clients) {
|
|
36
44
|
const subFolder = normalizeName(getClassicalClientName(client), NameType.File);
|
|
37
45
|
const clientTestFolder = joinPaths(baseTestFolder, subFolder);
|
|
38
|
-
if (
|
|
39
|
-
|
|
46
|
+
if (await dirExists(clientTestFolder)) {
|
|
47
|
+
await host.rm(clientTestFolder, { recursive: true });
|
|
40
48
|
}
|
|
41
49
|
}
|
|
42
50
|
} else {
|
|
43
51
|
// Single client, clean up the entire test/generated folder
|
|
44
|
-
if (
|
|
45
|
-
|
|
52
|
+
if (await dirExists(baseTestFolder)) {
|
|
53
|
+
await host.rm(baseTestFolder, { recursive: true });
|
|
46
54
|
}
|
|
47
55
|
}
|
|
48
56
|
}
|
|
@@ -50,9 +58,14 @@ async function cleanupTestFolder(dpgContext: SdkContext) {
|
|
|
50
58
|
/**
|
|
51
59
|
* Helpers to emit tests similar to samples
|
|
52
60
|
*/
|
|
53
|
-
export async function emitTests(
|
|
61
|
+
export async function emitTests(
|
|
62
|
+
dpgContext: SdkContext,
|
|
63
|
+
host?: CompilerHost,
|
|
64
|
+
): Promise<SourceFile[]> {
|
|
54
65
|
// Clean up the test/generated folder before generating new tests
|
|
55
|
-
|
|
66
|
+
if (host) {
|
|
67
|
+
await cleanupTestFolder(dpgContext, host);
|
|
68
|
+
}
|
|
56
69
|
|
|
57
70
|
return iterateClientsAndMethods(dpgContext, emitMethodTests);
|
|
58
71
|
}
|
|
@@ -30,7 +30,7 @@ export function buildClientDefinitions(model: RLCModel) {
|
|
|
30
30
|
importedResponses: new Set<string>(),
|
|
31
31
|
clientImports: new Set<string>(),
|
|
32
32
|
};
|
|
33
|
-
const project = new Project();
|
|
33
|
+
const project = new Project({ useInMemoryFileSystem: true });
|
|
34
34
|
const srcPath = model.srcPath;
|
|
35
35
|
const filePath = joinPaths(srcPath, `clientDefinitions.ts`);
|
|
36
36
|
const clientDefinitionsFile = project.createSourceFile(filePath, undefined, {
|
|
@@ -62,7 +62,7 @@ function getClientOptionsInterface(
|
|
|
62
62
|
export function buildClient(model: RLCModel): File | undefined {
|
|
63
63
|
const name = normalizeName(model.libraryName, NameType.File);
|
|
64
64
|
const { srcPath } = model;
|
|
65
|
-
const project = new Project();
|
|
65
|
+
const project = new Project({ useInMemoryFileSystem: true });
|
|
66
66
|
const filePath = joinPaths(srcPath, `${name}.ts`);
|
|
67
67
|
const clientFile = project.createSourceFile(filePath, undefined, {
|
|
68
68
|
overwrite: true,
|
|
@@ -23,7 +23,7 @@ import { RLCModel } from "./interfaces.js";
|
|
|
23
23
|
export function buildIndexFile(model: RLCModel) {
|
|
24
24
|
const multiClient = Boolean(model.options?.multiClient),
|
|
25
25
|
batch = model.options?.batch;
|
|
26
|
-
const project = new Project();
|
|
26
|
+
const project = new Project({ useInMemoryFileSystem: true });
|
|
27
27
|
const { srcPath } = model;
|
|
28
28
|
const filePath = joinPaths(srcPath, `index.ts`);
|
|
29
29
|
const indexFile = project.createSourceFile(filePath, undefined, {
|
|
@@ -15,7 +15,7 @@ export function buildIsUnexpectedHelper(model: RLCModel) {
|
|
|
15
15
|
if (!hasUnexpectedHelper(model)) {
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
|
-
const project = new Project();
|
|
18
|
+
const project = new Project({ useInMemoryFileSystem: true });
|
|
19
19
|
const srcPath = model.srcPath;
|
|
20
20
|
const filePath = joinPaths(srcPath, `isUnexpected.ts`);
|
|
21
21
|
const isErrorHelper = project.createSourceFile(filePath, undefined, {
|
|
@@ -13,7 +13,7 @@ export function buildLogger(model: RLCModel) {
|
|
|
13
13
|
if (model.options.flavor !== "azure") {
|
|
14
14
|
return undefined;
|
|
15
15
|
}
|
|
16
|
-
const project = new Project();
|
|
16
|
+
const project = new Project({ useInMemoryFileSystem: true });
|
|
17
17
|
const { srcPath, rlcSourceDir } = model;
|
|
18
18
|
const { packageDetails } = model.options;
|
|
19
19
|
const filePath = joinPaths(
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
} from "./interfaces.js";
|
|
28
28
|
|
|
29
29
|
export function buildParameterTypes(model: RLCModel) {
|
|
30
|
-
const project = new Project();
|
|
30
|
+
const project = new Project({ useInMemoryFileSystem: true });
|
|
31
31
|
const srcPath = model.srcPath;
|
|
32
32
|
const filePath = joinPaths(srcPath, `parameters.ts`);
|
|
33
33
|
const partialBodyTypeNames = new Set<string>();
|
|
@@ -19,7 +19,7 @@ import { ResponseHeaderSchema, ResponseMetadata, RLCModel } from "./interfaces.j
|
|
|
19
19
|
|
|
20
20
|
let hasErrorResponse = false;
|
|
21
21
|
export function buildResponseTypes(model: RLCModel) {
|
|
22
|
-
const project = new Project();
|
|
22
|
+
const project = new Project({ useInMemoryFileSystem: true });
|
|
23
23
|
const srcPath = model.srcPath;
|
|
24
24
|
const filePath = joinPaths(srcPath, `responses.ts`);
|
|
25
25
|
hasErrorResponse = false;
|
|
@@ -17,7 +17,7 @@ import { RLCModel, SchemaContext } from "./interfaces.js";
|
|
|
17
17
|
*/
|
|
18
18
|
export function buildSchemaTypes(model: RLCModel) {
|
|
19
19
|
const { srcPath } = model;
|
|
20
|
-
const project = new Project();
|
|
20
|
+
const project = new Project({ useInMemoryFileSystem: true });
|
|
21
21
|
let filePath = joinPaths(srcPath, `models.ts`);
|
|
22
22
|
const inputModelFile = generateModelFiles(model, project, filePath, [SchemaContext.Input]);
|
|
23
23
|
filePath = joinPaths(srcPath, `outputModels.ts`);
|
|
@@ -14,7 +14,7 @@ export function buildTopLevelIndex(model: RLCModel) {
|
|
|
14
14
|
if (!model.options) {
|
|
15
15
|
return undefined;
|
|
16
16
|
}
|
|
17
|
-
const project = new Project();
|
|
17
|
+
const project = new Project({ useInMemoryFileSystem: true });
|
|
18
18
|
const { srcPath } = model;
|
|
19
19
|
const { multiClient } = model.options;
|
|
20
20
|
const batch = model.options.batch;
|
|
@@ -6,7 +6,7 @@ import { RLCModel } from "../interfaces.js";
|
|
|
6
6
|
|
|
7
7
|
export function buildApiExtractorConfig(model: RLCModel) {
|
|
8
8
|
const { packageDetails, isModularLibrary, generateTest, azureSdkForJs } = model.options || {};
|
|
9
|
-
const project = new Project();
|
|
9
|
+
const project = new Project({ useInMemoryFileSystem: true });
|
|
10
10
|
|
|
11
11
|
let mainEntryPointFilePath = "dist/esm/index.d.ts";
|
|
12
12
|
|
|
@@ -65,7 +65,7 @@ export function buildEsLintConfig(model: RLCModel) {
|
|
|
65
65
|
if (model.options?.flavor !== "azure") {
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
68
|
-
const project = new Project();
|
|
68
|
+
const project = new Project({ useInMemoryFileSystem: true });
|
|
69
69
|
const filePath = "eslint.config.mjs";
|
|
70
70
|
|
|
71
71
|
let template: string;
|
|
@@ -66,7 +66,7 @@ export function buildPackageFile(
|
|
|
66
66
|
packageInfo = buildAzureStandalonePackage(extendedConfig);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
const project = new Project();
|
|
69
|
+
const project = new Project({ useInMemoryFileSystem: true });
|
|
70
70
|
const filePath = "package.json";
|
|
71
71
|
|
|
72
72
|
if (!packageInfo) {
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
5
5
|
// @ts-ignore: to fix the handlebars issue
|
|
6
|
-
import { readFileSync } from "fs";
|
|
7
6
|
import hbs from "handlebars";
|
|
8
7
|
import { getClientName } from "../helpers/name-constructors.js";
|
|
9
8
|
import { NameType, normalizeName } from "../helpers/name-utils.js";
|
|
@@ -363,10 +362,9 @@ export function buildReadmeFile(model: RLCModel) {
|
|
|
363
362
|
};
|
|
364
363
|
}
|
|
365
364
|
|
|
366
|
-
export function hasClientNameChanged(model: RLCModel,
|
|
365
|
+
export function hasClientNameChanged(model: RLCModel, existingReadmeContent: string): boolean {
|
|
367
366
|
try {
|
|
368
|
-
const
|
|
369
|
-
const importMatch = existingContent.match(
|
|
367
|
+
const importMatch = existingReadmeContent.match(
|
|
370
368
|
/import\s*\{\s*([A-Za-z0-9_]+)\s*\}\s*from\s*["'][^"']*["']/,
|
|
371
369
|
);
|
|
372
370
|
const existingClientName = importMatch?.[1];
|
|
@@ -379,21 +377,20 @@ export function hasClientNameChanged(model: RLCModel, existingReadmeFilePath: st
|
|
|
379
377
|
|
|
380
378
|
export function updateReadmeFile(
|
|
381
379
|
model: RLCModel,
|
|
382
|
-
|
|
380
|
+
existingReadmeContent: string,
|
|
383
381
|
): { path: string; content: string } | undefined {
|
|
384
382
|
try {
|
|
385
|
-
const existingContent = readFileSync(existingReadmeFilePath, "utf8");
|
|
386
383
|
const metadata = createMetadata(model) ?? {};
|
|
387
384
|
|
|
388
385
|
const newApiRefLink = hbs.compile(apiReferenceTemplate, { noEscape: true })(metadata).trim();
|
|
389
386
|
|
|
390
387
|
if (!newApiRefLink) {
|
|
391
|
-
return { path: "README.md", content:
|
|
388
|
+
return { path: "README.md", content: existingReadmeContent };
|
|
392
389
|
}
|
|
393
390
|
|
|
394
391
|
const apiRefRegex =
|
|
395
392
|
/^- \[API reference documentation\]\(https:\/\/learn\.microsoft\.com\/javascript\/api\/[^)]+\)$/m;
|
|
396
|
-
const updatedContent =
|
|
393
|
+
const updatedContent = existingReadmeContent.replace(apiRefRegex, (match) =>
|
|
397
394
|
match ? newApiRefLink : match,
|
|
398
395
|
);
|
|
399
396
|
|
|
@@ -12,7 +12,7 @@ export function buildRollupConfig(model: RLCModel) {
|
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
const project = new Project();
|
|
15
|
+
const project = new Project({ useInMemoryFileSystem: true });
|
|
16
16
|
const filePath = "rollup.config.js";
|
|
17
17
|
const rollupFile = project.createSourceFile(filePath, undefined, {
|
|
18
18
|
overwrite: true,
|
|
@@ -14,7 +14,7 @@ export function buildTsConfig(model: RLCModel) {
|
|
|
14
14
|
const { packageDetails, azureSdkForJs } = model.options || {};
|
|
15
15
|
const { generateTest, generateSample, generateReactNativeTarget } = model.options || {};
|
|
16
16
|
const clientPackageName = packageDetails?.name ?? "";
|
|
17
|
-
const project = new Project();
|
|
17
|
+
const project = new Project({ useInMemoryFileSystem: true });
|
|
18
18
|
|
|
19
19
|
let tsConfig: Record<string, any>;
|
|
20
20
|
|
package/src/utils/emit-util.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { CompilerHost, getDirectoryPath, joinPaths, NoTarget, Program } from "@typespec/compiler";
|
|
2
2
|
import { format } from "prettier";
|
|
3
|
+
import prettierPluginBabel from "prettier/plugins/babel";
|
|
4
|
+
import prettierPluginEstree from "prettier/plugins/estree";
|
|
5
|
+
import prettierPluginTypescript from "prettier/plugins/typescript";
|
|
3
6
|
import { prettierJSONOptions, prettierTypeScriptOptions, reportDiagnostic } from "../lib.js";
|
|
4
7
|
import {
|
|
5
8
|
buildSchemaTypes,
|
|
@@ -70,10 +73,10 @@ async function emitFile(
|
|
|
70
73
|
// Format the contents if necessary
|
|
71
74
|
if (isJson || isSourceCode) {
|
|
72
75
|
try {
|
|
73
|
-
prettierFileContent = await format(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
);
|
|
76
|
+
prettierFileContent = await format(prettierFileContent, {
|
|
77
|
+
...(isJson ? prettierJSONOptions : prettierTypeScriptOptions),
|
|
78
|
+
plugins: [prettierPluginTypescript, prettierPluginEstree, prettierPluginBabel],
|
|
79
|
+
});
|
|
77
80
|
} catch (e) {
|
|
78
81
|
reportDiagnostic(program, {
|
|
79
82
|
code: "file-formatting-error",
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import { NoTarget, Program, resolvePath } from "@typespec/compiler";
|
|
2
|
-
import { mkdir, readdir, rm, stat } from "fs/promises";
|
|
1
|
+
import { NoTarget, Program, resolvePath, type CompilerHost } from "@typespec/compiler";
|
|
3
2
|
import { reportDiagnostic } from "../lib.js";
|
|
4
3
|
|
|
5
|
-
export async function pathExists(targetPath: string): Promise<boolean> {
|
|
4
|
+
export async function pathExists(host: CompilerHost, targetPath: string): Promise<boolean> {
|
|
6
5
|
try {
|
|
7
|
-
await stat(targetPath);
|
|
6
|
+
await host.stat(targetPath);
|
|
8
7
|
return true;
|
|
9
8
|
} catch {
|
|
10
9
|
return false;
|
|
11
10
|
}
|
|
12
11
|
}
|
|
13
12
|
|
|
14
|
-
export async function emptyDir(dirPath: string): Promise<void> {
|
|
13
|
+
export async function emptyDir(host: CompilerHost, dirPath: string): Promise<void> {
|
|
15
14
|
let entries: string[];
|
|
16
15
|
try {
|
|
17
|
-
entries = await
|
|
16
|
+
entries = await host.readDir(dirPath);
|
|
18
17
|
} catch {
|
|
19
|
-
await
|
|
18
|
+
await host.mkdirp(dirPath);
|
|
20
19
|
return;
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
await Promise.all(
|
|
24
|
-
entries.map((entry) => rm(resolvePath(dirPath, entry), { recursive: true
|
|
23
|
+
entries.map((entry) => host.rm(resolvePath(dirPath, entry), { recursive: true })),
|
|
25
24
|
);
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
export async function clearDirectory(
|
|
28
|
+
host: CompilerHost,
|
|
29
29
|
dirPath: string,
|
|
30
30
|
excludeNames: string[] = [],
|
|
31
31
|
program?: Program,
|
|
32
32
|
): Promise<void> {
|
|
33
|
-
if (!(await pathExists(dirPath))) {
|
|
33
|
+
if (!(await pathExists(host, dirPath))) {
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
// If no exclude names, just use regular emptyDir for efficiency
|
|
38
38
|
if (excludeNames.length === 0) {
|
|
39
|
-
await emptyDir(dirPath);
|
|
39
|
+
await emptyDir(host, dirPath);
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
try {
|
|
44
44
|
// Get all subdirectories and files
|
|
45
|
-
const entries = await
|
|
45
|
+
const entries = await host.readDir(dirPath);
|
|
46
46
|
|
|
47
47
|
// Filter entries to exclude those that should be preserved
|
|
48
48
|
const filteredEntries = entries.filter((entry) => {
|
|
@@ -52,7 +52,7 @@ export async function clearDirectory(
|
|
|
52
52
|
// Process each entry
|
|
53
53
|
for (const entry of filteredEntries) {
|
|
54
54
|
const entryPath = resolvePath(dirPath, entry);
|
|
55
|
-
await rm(entryPath, { recursive: true
|
|
55
|
+
await host.rm(entryPath, { recursive: true });
|
|
56
56
|
}
|
|
57
57
|
} catch (error) {
|
|
58
58
|
// If there's an error, fall back to regular emptyDir
|
|
@@ -63,6 +63,6 @@ export async function clearDirectory(
|
|
|
63
63
|
target: NoTarget,
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
|
-
await emptyDir(dirPath);
|
|
66
|
+
await emptyDir(host, dirPath);
|
|
67
67
|
}
|
|
68
68
|
}
|