From bca3cc79fd5847528f8e82dc6c54e1b7aec193c9 Mon Sep 17 00:00:00 2001 From: Dmitry Zhukovsky Date: Sat, 28 Jun 2025 21:19:22 +0200 Subject: [PATCH 1/5] Enhance build process and module exports - Update .gitignore to include esm/ directory - Modify buildpackage.js to clean and compile TypeScript for both bin and esm directories - Add generateExports.js to dynamically update package.json exports - Remove rename Context.d.ts file to Context.ts - Update package.json to include new scripts and dependencies - Create tsconfig.esm.json for ESM module compilation --- .gitignore | 1 + buildpackage.js | 39 ++++++++++++++--------- generateExports.js | 41 +++++++++++++++++++++++++ package.json | 6 +++- src/Common/{Context.d.ts => Context.ts} | 0 tsconfig.esm.json | 9 ++++++ 6 files changed, 80 insertions(+), 16 deletions(-) create mode 100644 generateExports.js rename src/Common/{Context.d.ts => Context.ts} (100%) create mode 100644 tsconfig.esm.json diff --git a/.gitignore b/.gitignore index be267c21..75556de7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ npm-debug.log node_modules/ bin/ +/esm/ apis-config/ \ No newline at end of file diff --git a/buildpackage.js b/buildpackage.js index e9ad1fd9..a8174c51 100644 --- a/buildpackage.js +++ b/buildpackage.js @@ -13,13 +13,16 @@ const UglifyES = require("uglify-es"); (async function() { // Clean bin directory - console.log("# Cleaning bin. Running shelljs rm -rf ./bin"); - shell.rm("-rf", "./bin"); + console.log("# Cleaning bin/ and esm/. Running shelljs rm -rf ./bin ./esm"); + shell.rm("-rf", "./bin", "./esm"); - // Compile typescript - console.log("# Compiling TypeScript. Executing `node_modules\\.bin\\tsc -p ./tsconfig.json`."); + // On Windows, we need `\`, on Unix we need `/`. + const tsc = path.join("node_modules", ".bin", "tsc"); + + // Compile typescript ./bin folder + console.log(`# Compiling TypeScript. Executing \`${tsc} -p ./tsconfig.json\`.`); try { - execSync("node_modules\\.bin\\tsc -p ./tsconfig.json", { + execSync(`${tsc} -p ./tsconfig.json`, { stdio: [0, 1, 2], shell: true, cwd: __dirname, @@ -29,18 +32,24 @@ const UglifyES = require("uglify-es"); process.exit(1); } - // Copy ts files to bin - console.log("# Copy declare files to bin."); + // Compile typescript ./esm folder + console.log(`# Compiling TypeScript to ESM. Executing \`${tsc} -p ./tsconfig.esm.json\`.`); try { - await copy(path.join(__dirname, "src"), path.join(__dirname, "bin"), { - filter: f => { - return f.endsWith(".d.ts"); - }, + execSync(`${tsc} -p ./tsconfig.esm.json`, { + stdio: [0, 1, 2], + shell: true, + cwd: __dirname, }); - } catch (e) { - console.log("Copy failed. " + error); + } catch (error) { + console.log("ERROR: Failed to build TypeScript ESM."); + process.exit(1); } - + console.log("# Creating ./esm/package.json to show files in ./esm are ESM"); + fs.writeFileSync("./esm/package.json", '{"type": "module"}\n'); + // Move ./esm folder into ./bin/esm + console.log("# Moving ./esm folder to ./bin/esm"); + fs.renameSync("./esm", "./bin/esm"); + // Uglify JavaScript console.log("# Minifying JS using the UglifyES API, replacing un-minified files."); let count = 0; @@ -56,7 +65,7 @@ const UglifyES = require("uglify-es"); }); for (const file of files) { - if (file.includes("node_modules/")) { + if (file.includes("node_modules/") || file.includes("esm/")) { continue; } fs.writeFileSync( diff --git a/generateExports.js b/generateExports.js new file mode 100644 index 00000000..2c734b40 --- /dev/null +++ b/generateExports.js @@ -0,0 +1,41 @@ +import { promises as fs } from "fs"; + +const srcDir = "src"; +const esmDir = "esm"; + +// Read all subdirectories in src +const entries = await fs.readdir(srcDir, { withFileTypes: true }); +const folders = entries.filter((e) => e.isDirectory()).map((e) => e.name); + +// Base exports +const exportsField = { + ".": { + import: `./${esmDir}/index.js`, + default: "./index.js", + }, + "./*": { + import: `./${esmDir}/*/index.js`, + default: "./*/index.js", + }, + "./package.json": "./package.json", +}; + +// Add exports for each subfolder +for (const folder of folders) { + exportsField[`./${folder}/*`] = { + import: `./${esmDir}/${folder}/*.js`, + default: `./${folder}/*.js`, + }; + exportsField[`./${folder}/*.js`] = { + import: `./${esmDir}/${folder}/*.js`, + default: `./${folder}/*.js`, + }; +} + +// Update package.json +const pkgPath = "package.json"; +const pkg = JSON.parse(await fs.readFile(pkgPath, "utf8")); +pkg.exports = exportsField; +await fs.writeFile(pkgPath, JSON.stringify(pkg, null, 2)); + +console.log("✅ package.json exports updated from src/"); diff --git a/package.json b/package.json index 863de58e..0c348b57 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,10 @@ "type": "git", "url": "https://github.com/Microsoft/azure-devops-extension-api.git" }, + "type": "module", "scripts": { + "generate:exports": "node generateExports.js", + "prepare": "npm run generate:exports && tsc --project tsconfig.esm.json", "build": "node buildpackage.js" }, "keywords": [ @@ -21,7 +24,8 @@ }, "homepage": "https://docs.microsoft.com/en-us/azure/devops/integrate", "dependencies": { - "whatwg-fetch": "~3.0.0" + "whatwg-fetch": "~3.0.0", + "azure-devops-extension-sdk": "^4.0.2" }, "peerDependencies": { "azure-devops-extension-sdk": "^2 || ^3 || ^4" diff --git a/src/Common/Context.d.ts b/src/Common/Context.ts similarity index 100% rename from src/Common/Context.d.ts rename to src/Common/Context.ts diff --git a/tsconfig.esm.json b/tsconfig.esm.json new file mode 100644 index 00000000..4492a99f --- /dev/null +++ b/tsconfig.esm.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "target": "es2015", + "rootDir": "src/", + "outDir": "esm/", + "module": "es2015", + } +} \ No newline at end of file From b7b2c56e4c9eadb65c64c98cc043cdf6dcfa468f Mon Sep 17 00:00:00 2001 From: Dmitry Zhukovsky Date: Sat, 28 Jun 2025 21:24:07 +0200 Subject: [PATCH 2/5] Refactor generateExports.js to use synchronous file reading for improved performance --- generateExports.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/generateExports.js b/generateExports.js index 2c734b40..ebd739b1 100644 --- a/generateExports.js +++ b/generateExports.js @@ -1,10 +1,10 @@ -import { promises as fs } from "fs"; +const fs = require("fs"); const srcDir = "src"; const esmDir = "esm"; // Read all subdirectories in src -const entries = await fs.readdir(srcDir, { withFileTypes: true }); +const entries = fs.readdirSync(srcDir, { withFileTypes: true }); const folders = entries.filter((e) => e.isDirectory()).map((e) => e.name); // Base exports From 41a59a2086cf624522d98b1037797474163ae46e Mon Sep 17 00:00:00 2001 From: Dmitry Zhukovsky Date: Sat, 28 Jun 2025 21:25:53 +0200 Subject: [PATCH 3/5] Refactor generateExports.js to use synchronous file reading for package.json updates --- generateExports.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/generateExports.js b/generateExports.js index ebd739b1..8645c736 100644 --- a/generateExports.js +++ b/generateExports.js @@ -34,8 +34,8 @@ for (const folder of folders) { // Update package.json const pkgPath = "package.json"; -const pkg = JSON.parse(await fs.readFile(pkgPath, "utf8")); +const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8")); pkg.exports = exportsField; -await fs.writeFile(pkgPath, JSON.stringify(pkg, null, 2)); +fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2)); console.log("✅ package.json exports updated from src/"); From 08977d6fc628f8fdc4c268e15f77e5759cdbabe6 Mon Sep 17 00:00:00 2001 From: Dmitry Zhukovsky Date: Sat, 28 Jun 2025 21:37:16 +0200 Subject: [PATCH 4/5] Remove "type": "module" from package.json for compatibility --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 0c348b57..53830d2a 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,6 @@ "type": "git", "url": "https://github.com/Microsoft/azure-devops-extension-api.git" }, - "type": "module", "scripts": { "generate:exports": "node generateExports.js", "prepare": "npm run generate:exports && tsc --project tsconfig.esm.json", From fc02fd99c03a04d84704362df651d6d6f62dc5f8 Mon Sep 17 00:00:00 2001 From: Dmitry Zhukovsky Date: Sat, 28 Jun 2025 21:45:57 +0200 Subject: [PATCH 5/5] Fix import statement in BoardsClient.ts for proper module syntax --- package.json | 1 + src/Boards/BoardsClient.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 53830d2a..0c348b57 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "type": "git", "url": "https://github.com/Microsoft/azure-devops-extension-api.git" }, + "type": "module", "scripts": { "generate:exports": "node generateExports.js", "prepare": "npm run generate:exports && tsc --project tsconfig.esm.json", diff --git a/src/Boards/BoardsClient.ts b/src/Boards/BoardsClient.ts index 33e36436..f4b18899 100644 --- a/src/Boards/BoardsClient.ts +++ b/src/Boards/BoardsClient.ts @@ -7,7 +7,7 @@ import { IVssRestClientOptions } from "../Common/Context"; import { RestClientBase } from "../Common/RestClientBase"; -import Boards = require("../Boards/Boards"); +import * as Boards from '../Boards/Boards'; export class BoardsRestClient extends RestClientBase { constructor(options: IVssRestClientOptions) {