Skip to content

Commit

Permalink
merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
kazrael2119 committed Jan 17, 2025
2 parents 2a853bb + 59cdd73 commit 212ef63
Show file tree
Hide file tree
Showing 8,010 changed files with 63,609 additions and 53,087 deletions.
The diff you're trying to view is too large. We only load the first 3000 changed files.
4,933 changes: 2,669 additions & 2,264 deletions common/config/rush/pnpm-lock.yaml

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions common/tools/dev-tool/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@
"@_ts/max": "npm:typescript@latest",
"@_ts/min": "npm:typescript@~4.2.4",
"@azure/identity": "^4.4.1",
"@microsoft/api-extractor": "^7.42.3",
"@microsoft/api-extractor-model": "^7.29.8",
"@microsoft/api-extractor": "^7.49.1",
"@microsoft/api-extractor-model": "^7.30.2",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-inject": "^5.0.5",
"@rollup/plugin-json": "^6.0.1",
Expand Down
32 changes: 20 additions & 12 deletions common/tools/dev-tool/src/commands/admin/migrate-package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,18 @@ const VITEST_CONFIG = `
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { defineConfig, mergeConfig } from "vitest/config";
import viteConfig from "../../../vitest.shared.config.ts";
export default viteConfig;
export default mergeConfig(
viteConfig,
defineConfig({
test: {
testTimeout: 1200000,
hookTimeout: 1200000,
},
}),
);
`;

const VITEST_BROWSER_CONFIG = `
Expand All @@ -166,9 +175,9 @@ export default mergeConfig(
viteConfig,
defineConfig({
test: {
include: [
"dist-test/browser/test/**/*.spec.js",
],
include: ["dist-test/browser/test/**/*.spec.js",],
testTimeout: 1200000,
hookTimeout: 1200000,
},
}),
);
Expand All @@ -190,14 +199,7 @@ export default mergeConfig(

async function writeBrowserTestConfig(packageFolder: string): Promise<void> {
const testConfig = {
extends: "./.tshy/build.json",
include: ["./src/**/*.ts", "./src/**/*.mts", "./test/**/*.spec.ts", "./test/**/*.mts"],
exclude: ["./test/**/node/**/*.ts"],
compilerOptions: {
outDir: "./dist-test/browser",
rootDir: ".",
skipLibCheck: true,
},
extends: ["./tsconfig.test.json", "../../../tsconfig.browser.base.json"],
};

await saveJson(resolve(packageFolder, "tsconfig.browser.config.json"), testConfig);
Expand Down Expand Up @@ -340,6 +342,7 @@ function setScriptsSection(
scripts["unit-test:node"] = "dev-tool run test:vitest";

if (options.isArm) {
scripts["unit-test:browser"] = "echo skipped";
scripts["integration-test:node"] = "dev-tool run test:vitest --esm";
}

Expand Down Expand Up @@ -415,6 +418,11 @@ async function addNewPackages(packageJson: any, options: { browser: boolean }):
packageJson.devDependencies[newPackage] = `^${latestVersion.replace("\n", "")}`;
}

packageJson.devDependencies["vitest"] = "^2.1.8";
packageJson.devDependencies["@vitest/coverage-istanbul"] = "^2.1.8";
if (options.browser) {
packageJson.devDependencies["@vitest/browser"] = "^2.1.8";
}
const packagesToUpdate = [
{ package: "@azure-tools/test-credential", version: "^2.0.0" },
{ package: "@azure-tools/test-recorder", version: "^4.1.0" },
Expand Down
102 changes: 82 additions & 20 deletions common/tools/dev-tool/src/commands/run/update-snippets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,15 +229,12 @@ async function parseSnippetDefinitions(
sourceFile,
);

const imports: [string, string][] = [];
const imports: { name: string; moduleSpecifier: string; isDefault: boolean }[] = [];

// This nested visitor is just for extracting the imports of a symbol.
const symbolImportVisitor: ts.Visitor = (node: ts.Node) => {
let importLocations: string[] | undefined;
if (
ts.isIdentifier(node) &&
(importLocations = extractImportLocations(node)) !== undefined
) {
if (ts.isIdentifier(node)) {
const importLocations = extractImportLocations(node);
if (importLocations.length > 1) {
// We can probably handle this, but it's an obscure case and it's probably better to let it error out and
// then observe whether or not we actually need (or even _want_) snippets with merged imports.
Expand All @@ -247,7 +244,10 @@ async function parseSnippetDefinitions(
} else if (importLocations.length === 1) {
// The symbol was imported, so we need to track the imports to add them to the snippet later.
log.debug(`symbol ${node.text} was imported from ${importLocations[0]}`);
imports.push([node.text, importLocations[0]]);
imports.push({
name: node.text,
...importLocations[0],
});
}
// else the symbol was not imported within this file, so it must be defined in the ambient context of the
// module, so we don't need to generate any code for it.
Expand All @@ -264,23 +264,56 @@ async function parseSnippetDefinitions(
// file using `convert`.
log.debug(`found a snippet named ${name.text}: \n${contents}`);

interface ImportedSymbols {
default?: string;
named?: Set<string>;
}

// We have a loose map of imports in the form { [k:symbol]: module } and we need to anneal it into a map
// { [k: module]: symbol[] } (one import statement per module with the whole list of symbols imported from it)
const importMap = new Map<string, Set<string>>(
imports.map(([, module]) => [module, new Set()]),
);
const importMap = new Map<string, ImportedSymbols>();

for (const [symbol, name] of imports) {
importMap.get(name)!.add(symbol);
for (const { name, moduleSpecifier, isDefault } of imports) {
let moduleImports = importMap.get(moduleSpecifier);
if (!moduleImports) {
moduleImports = {};
importMap.set(moduleSpecifier, moduleImports);
}
if (isDefault) {
if (moduleImports.default) {
throw new Error(
`unrecoverable error: multiple default imports from the same module '${moduleSpecifier}'`,
);
}
moduleImports.default = name;
} else {
if (!moduleImports.named) {
moduleImports.named = new Set();
}
moduleImports.named.add(name);
}
}

// Form import declarations and prepend them to the rest of the contents.
const fullSnippetTypeScriptText = (
[...importMap.entries()]
.map(
([module, symbols]) =>
`import { ${[...symbols.values()].join(", ")} } from "${module}";`,
)
.map(([module, imports]) => {
const importParts = [];
if (imports.default) {
importParts.push(imports.default);
}
if (imports.named) {
importParts.push(`{ ${[...imports.named].join(", ")} }`);
}

if (importParts.length === 0) {
throw new Error(
`unrecoverable error: no imports were generated for the snippet '${name.text}'`,
);
}

return `import ${importParts.join(", ")} from "${module}";`;
})
.join(EOL) +
EOL +
EOL +
Expand Down Expand Up @@ -387,11 +420,14 @@ async function parseSnippetDefinitions(
* @param node - the node to check for imports
* @returns a list of module specifiers that form the definition of the node's symbol, or undefined
*/
function extractImportLocations(node: ts.Node): string[] | undefined {
function extractImportLocations(node: ts.Node): {
isDefault: boolean;
moduleSpecifier: string;
}[] {
const sym = checker.getSymbolAtLocation(node);

// Get all the decls that are in source files and where the decl comes from an import clause.
return sym?.declarations
const nonDefaultExports = sym?.declarations
?.filter(
(decl) =>
decl.getSourceFile() === sourceFile &&
Expand All @@ -411,12 +447,38 @@ async function parseSnippetDefinitions(
moduleSpecifierText === path.join(relativeIndexPath, "index.js") ||
moduleSpecifierText === path.join(relativeIndexPath, "index")
) {
return project.name;
return { moduleSpecifier: project.name, isDefault: false };
} else {
return moduleSpecifierText;
return { moduleSpecifier: moduleSpecifierText, isDefault: false };
}
},
);

const defaultExports = sym?.declarations
?.filter(
(decl) =>
decl.getSourceFile() === sourceFile &&
ts.isImportClause(decl) &&
ts.isImportDeclaration(decl.parent) &&
decl.name,
)
.map((decl) => {
const moduleSpecifierText = (
(decl.parent as ts.ImportDeclaration).moduleSpecifier as ts.StringLiteral
).text;

if (
moduleSpecifierText === relativeIndexPath ||
moduleSpecifierText === path.join(relativeIndexPath, "index.js") ||
moduleSpecifierText === path.join(relativeIndexPath, "index")
) {
return { moduleSpecifier: project.name, isDefault: true };
} else {
return { moduleSpecifier: moduleSpecifierText, isDefault: true };
}
});

return [...(nonDefaultExports ?? []), ...(defaultExports ?? [])];
}
}

Expand Down
2 changes: 1 addition & 1 deletion common/tools/dev-tool/src/templates/sampleReadme.md.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ async function urlExists(url: string): Promise<boolean> {
async function createApiRef(info: SampleReadmeConfiguration): Promise<string> {
if (info.apiRefLink) {
return `[apiref]: ${info.apiRefLink}`;
} else if (info.scope.startsWith("@azure")) {
} else if (info.scope?.startsWith("@azure")) {
const link = `https://learn.microsoft.com/javascript/api/${info.scope}/${info.baseName}${info.isBeta ? "?view=azure-node-preview" : ""}`;
if (await urlExists(link)) {
return `[apiref]: ${link}`;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,45 @@
import { existsSync, lstatSync } from "node:fs";
import { resolve } from "node:path";
import { SourceFile } from "ts-morph";
import { CallExpression, FunctionExpression, SourceFile, SyntaxKind } from "ts-morph";

export default function fixSourceFile(sourceFile: SourceFile): void {
const sourceLinesToRemove = [
"const should = chai.should();",
"chai.use(chaiAsPromised);",
"chai.use(chaiExclude);",
"const expect = chai.expect;",
"dotenv.config();",
];
// Iterate over all the statements in the source file
for (const statement of sourceFile.getStatements()) {
// Remove old legacy lines
for (const line of sourceLinesToRemove) {
if (statement.getText() === line) {
statement.remove();
}
removeLegacyStatements(sourceFile);
fixVitestMethods(sourceFile);
fixLegacyStatements(sourceFile);
fixArrowFunctions(sourceFile);
fixFunctionReturnTypes(sourceFile);

// Iterate over all the import declarations
for (const importExportDeclaration of sourceFile.getImportDeclarations()) {
let moduleSpecifier = importExportDeclaration.getModuleSpecifierValue();
moduleSpecifier = fixDeclaration(sourceFile, moduleSpecifier);
importExportDeclaration.setModuleSpecifier(moduleSpecifier);
}

// iterate over all the export declarations
for (const exportDeclaration of sourceFile.getExportDeclarations()) {
let moduleSpecifier = exportDeclaration.getModuleSpecifierValue();
if (moduleSpecifier) {
moduleSpecifier = fixDeclaration(sourceFile, moduleSpecifier);
exportDeclaration.setModuleSpecifier(moduleSpecifier);
}
}
}

function fixFunctionReturnTypes(sourceFile: SourceFile): void {
const functionDeclarations = sourceFile.getDescendantsOfKind(SyntaxKind.FunctionDeclaration);

for (const funcDecl of functionDeclarations) {
if (
funcDecl.getModifiers().some((mod) => mod.getText() === "async") &&
!funcDecl.getReturnTypeNode()
) {
funcDecl.setReturnType("Promise<void>");
}
}
}

function fixLegacyStatements(sourceFile: SourceFile): void {
for (const statement of sourceFile.getStatements()) {
const patternsToReplace = [
{ pattern: /\(this: Suite\)/g, replace: "(ctx)" },
Expand All @@ -40,20 +60,71 @@ export default function fixSourceFile(sourceFile: SourceFile): void {
}
}
}
}

// Iterate over all the import declarations
for (const importExportDeclaration of sourceFile.getImportDeclarations()) {
let moduleSpecifier = importExportDeclaration.getModuleSpecifierValue();
moduleSpecifier = fixDeclaration(sourceFile, moduleSpecifier);
importExportDeclaration.setModuleSpecifier(moduleSpecifier);
function removeLegacyStatements(sourceFile: SourceFile): void {
const sourceLinesToRemove = [
"const should = chai.should();",
"chai.use(chaiAsPromised);",
"chai.use(chaiExclude);",
"const expect = chai.expect;",
"dotenv.config();",
];
// Iterate over all the statements in the source file
for (const statement of sourceFile.getStatements()) {
// Remove old legacy lines
for (const line of sourceLinesToRemove) {
if (statement.getText() === line) {
statement.remove();
}
}
}
}

// iterate over all the export declarations
for (const exportDeclaration of sourceFile.getExportDeclarations()) {
let moduleSpecifier = exportDeclaration.getModuleSpecifierValue();
if (moduleSpecifier) {
moduleSpecifier = fixDeclaration(sourceFile, moduleSpecifier);
exportDeclaration.setModuleSpecifier(moduleSpecifier);
function fixVitestMethods(sourceFile: SourceFile): void {
// Find all 'it' and 'it.skip' call expressions and check for 'timeout'
const itCalls = sourceFile
.getDescendantsOfKind(SyntaxKind.CallExpression)
.filter((callExpr: CallExpression) => {
const exprText = callExpr.getExpression().getText();
return exprText === "it" || exprText === "it.skip";
});

for (const itCall of itCalls) {
const timeoutCall = itCall
.getDescendantsOfKind(SyntaxKind.CallExpression)
.find((callExpr: CallExpression) => callExpr.getExpression().getText() === "timeout");

if (timeoutCall) {
const timeoutValue = timeoutCall.getArguments()[0].getText();
const itArguments = itCall.getArguments();
const testName = itArguments[0].getText();
const testFunction = itArguments[1].getText();

// Replace the 'it' call with the new format
itCall.replaceWithText(`it(${testName}, { timeout: ${timeoutValue} }, ${testFunction});`);
}
}
}

function fixArrowFunctions(sourceFile: SourceFile): void {
// Replace 'function' with arrow functions in 'beforeEach' and 'afterEach' calls
const lifecycleMethods = ["beforeEach", "afterEach"];
for (const method of lifecycleMethods) {
const methodCalls = sourceFile
.getDescendantsOfKind(SyntaxKind.CallExpression)
.filter((callExpr: CallExpression) => callExpr.getExpression().getText() === method);

for (const methodCall of methodCalls) {
const funcExpr = methodCall.getArguments()[0] as FunctionExpression;
if (funcExpr && funcExpr.getKind() === SyntaxKind.FunctionExpression) {
const params = funcExpr
.getParameters()
.map((param) => param.getText())
.join(", ");
const body = funcExpr.getBody().getText();
methodCall.getArguments()[0].replaceWithText(`async (${params}) => ${body}`);
}
}
}
}
Expand Down
Loading

0 comments on commit 212ef63

Please sign in to comment.