devops-plugin-kit 0.1.0

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 (112) hide show
  1. package/@danieli-automation/create-devops-plugin/package.json +30 -0
  2. package/@danieli-automation/create-devops-plugin/src/cli.ts +136 -0
  3. package/@danieli-automation/create-devops-plugin/src/index.ts +37 -0
  4. package/@danieli-automation/create-devops-plugin/src/template/files/apiIndexMock.ts +10 -0
  5. package/@danieli-automation/create-devops-plugin/src/template/files/appHtml.ts +25 -0
  6. package/@danieli-automation/create-devops-plugin/src/template/files/appStyles.ts +22 -0
  7. package/@danieli-automation/create-devops-plugin/src/template/files/appTsx.ts +41 -0
  8. package/@danieli-automation/create-devops-plugin/src/template/files/envExample.ts +11 -0
  9. package/@danieli-automation/create-devops-plugin/src/template/files/gitignore.ts +13 -0
  10. package/@danieli-automation/create-devops-plugin/src/template/files/index.ts +51 -0
  11. package/@danieli-automation/create-devops-plugin/src/template/files/packageJson.ts +60 -0
  12. package/@danieli-automation/create-devops-plugin/src/template/files/readme.ts +30 -0
  13. package/@danieli-automation/create-devops-plugin/src/template/files/sdkMock.ts +11 -0
  14. package/@danieli-automation/create-devops-plugin/src/template/files/testSetup.ts +16 -0
  15. package/@danieli-automation/create-devops-plugin/src/template/files/tsconfigJson.ts +30 -0
  16. package/@danieli-automation/create-devops-plugin/src/template/files/vitestConfig.ts +57 -0
  17. package/@danieli-automation/create-devops-plugin/src/template/files/webpackAppConfig.ts +31 -0
  18. package/@danieli-automation/create-devops-plugin/src/template/files/webpackCommonConfig.ts +116 -0
  19. package/@danieli-automation/create-devops-plugin/src/template/files/webpackConfig.ts +15 -0
  20. package/@danieli-automation/create-devops-plugin/src/template/files.ts +1 -0
  21. package/@danieli-automation/create-devops-plugin/src/template/fs.ts +85 -0
  22. package/@danieli-automation/create-devops-plugin/src/template/manifest.ts +40 -0
  23. package/@danieli-automation/create-devops-plugin/src/template/npm.ts +22 -0
  24. package/@danieli-automation/create-devops-plugin/src/template/static/fonts/AzDevMDL2.woff +0 -0
  25. package/@danieli-automation/create-devops-plugin/src/template/static/fonts/bowtie.woff2 +0 -0
  26. package/@danieli-automation/create-devops-plugin/src/template/static/fonts/fabric-icons.woff +0 -0
  27. package/@danieli-automation/create-devops-plugin/src/template/static/fonts/fluent-filled-v1.1.293.woff2 +0 -0
  28. package/@danieli-automation/create-devops-plugin/src/template/static/fonts/fluent-regular-v1.1.293.woff2 +0 -0
  29. package/@danieli-automation/create-devops-plugin/src/template/static/images/DigiMetLogo.jpeg +0 -0
  30. package/@danieli-automation/create-devops-plugin/src/template/static/images/danieliAutomation.png +0 -0
  31. package/@danieli-automation/create-devops-plugin/src/template/static/images/danieliAutomationBlack.jpg +0 -0
  32. package/@danieli-automation/create-devops-plugin/src/template/static/images/danieli_digi_met_logo.jpeg +0 -0
  33. package/@danieli-automation/create-devops-plugin/src/template/static/images/logoSmallpng.png +0 -0
  34. package/@danieli-automation/create-devops-plugin/src/template/types.ts +14 -0
  35. package/@danieli-automation/create-devops-plugin/src/template/utils.ts +22 -0
  36. package/@danieli-automation/create-devops-plugin/tsconfig.json +8 -0
  37. package/@danieli-automation/devops-plugin-core/package.json +27 -0
  38. package/@danieli-automation/devops-plugin-core/src/core/azureClients.ts +18 -0
  39. package/@danieli-automation/devops-plugin-core/src/core/storage/createStore.ts +65 -0
  40. package/@danieli-automation/devops-plugin-core/src/core/storage/hooks/useCrossTeamSprintInstance.ts +145 -0
  41. package/@danieli-automation/devops-plugin-core/src/core/storage/hooks/useTaskOrder.ts +125 -0
  42. package/@danieli-automation/devops-plugin-core/src/core/storage/hooks/useWorkItemOrder.ts +86 -0
  43. package/@danieli-automation/devops-plugin-core/src/core/storage/index.ts +13 -0
  44. package/@danieli-automation/devops-plugin-core/src/core/storage/keys.ts +31 -0
  45. package/@danieli-automation/devops-plugin-core/src/core/storage/repositories/instance.ts +184 -0
  46. package/@danieli-automation/devops-plugin-core/src/core/storage/repositories/taskOrder.ts +59 -0
  47. package/@danieli-automation/devops-plugin-core/src/core/storage/repositories/workItemOrder.ts +60 -0
  48. package/@danieli-automation/devops-plugin-core/src/core/storage/stores.ts +18 -0
  49. package/@danieli-automation/devops-plugin-core/src/core/types/AdoWorkItemType.ts +18 -0
  50. package/@danieli-automation/devops-plugin-core/src/core/types/KVStoreType.ts +1 -0
  51. package/@danieli-automation/devops-plugin-core/src/core/types/ScopeType.ts +1 -0
  52. package/@danieli-automation/devops-plugin-core/src/core/types/SelectedProjectType.ts +8 -0
  53. package/@danieli-automation/devops-plugin-core/src/core/types/SortConfigType.ts +2 -0
  54. package/@danieli-automation/devops-plugin-core/src/core/types/instance/CreateInstanceInputType.ts +10 -0
  55. package/@danieli-automation/devops-plugin-core/src/core/types/instance/CrossSprintInstanceType.ts +20 -0
  56. package/@danieli-automation/devops-plugin-core/src/core/types/instance/DefaultInstanceType.ts +12 -0
  57. package/@danieli-automation/devops-plugin-core/src/core/types/instance/InstanceRowType.ts +18 -0
  58. package/@danieli-automation/devops-plugin-core/src/core/types/instance/UpdateInstanceInputType.ts +10 -0
  59. package/@danieli-automation/devops-plugin-core/src/core/types/taskOrder/TaskOrderMapType.ts +3 -0
  60. package/@danieli-automation/devops-plugin-core/src/core/types/taskOrder/TaskOrderType.ts +1 -0
  61. package/@danieli-automation/devops-plugin-core/src/core/types/workItemOrder/WorkItemOrderMapType.ts +3 -0
  62. package/@danieli-automation/devops-plugin-core/src/core/types/workItemOrder/WorkItemOrderType.ts +1 -0
  63. package/@danieli-automation/devops-plugin-core/src/index.ts +1 -0
  64. package/@danieli-automation/devops-plugin-core/src/pluginCore.ts +12 -0
  65. package/@danieli-automation/devops-plugin-core/tsconfig.json +16 -0
  66. package/@danieli-automation/devops-plugin-features/package.json +31 -0
  67. package/@danieli-automation/devops-plugin-features/src/app/stores/useUIStore.ts +12 -0
  68. package/@danieli-automation/devops-plugin-features/src/app/utils/date.ts +9 -0
  69. package/@danieli-automation/devops-plugin-features/src/app/utils/global.ts +9 -0
  70. package/@danieli-automation/devops-plugin-features/src/core/azureClients.ts +12 -0
  71. package/@danieli-automation/devops-plugin-features/src/features/instances/constants/InstanceConstant.ts +5 -0
  72. package/@danieli-automation/devops-plugin-features/src/features/instances/hooks/useInstancePermission.ts +127 -0
  73. package/@danieli-automation/devops-plugin-features/src/features/instances/stores/__tests__/useInstanceStore.test.ts +25 -0
  74. package/@danieli-automation/devops-plugin-features/src/features/instances/stores/types/InstanceStoreType.ts +7 -0
  75. package/@danieli-automation/devops-plugin-features/src/features/instances/stores/useInstanceStore.ts +9 -0
  76. package/@danieli-automation/devops-plugin-features/src/features/instances/types/CrossSprintInstanceType.ts +20 -0
  77. package/@danieli-automation/devops-plugin-features/src/features/instances/utils/instance.ts +55 -0
  78. package/@danieli-automation/devops-plugin-features/src/features/iterations/api/iterations.ts +298 -0
  79. package/@danieli-automation/devops-plugin-features/src/features/iterations/services/IterationService.ts +215 -0
  80. package/@danieli-automation/devops-plugin-features/src/features/iterations/types/IterationInfoType.ts +15 -0
  81. package/@danieli-automation/devops-plugin-features/src/features/iterations/utils/__tests__/iteration.test.ts +39 -0
  82. package/@danieli-automation/devops-plugin-features/src/features/iterations/utils/iteration.ts +132 -0
  83. package/@danieli-automation/devops-plugin-features/src/features/teams/api/projects.ts +79 -0
  84. package/@danieli-automation/devops-plugin-features/src/features/teams/api/teams.ts +29 -0
  85. package/@danieli-automation/devops-plugin-features/src/features/teams/api/users.ts +124 -0
  86. package/@danieli-automation/devops-plugin-features/src/features/teams/services/ProjectService.ts +80 -0
  87. package/@danieli-automation/devops-plugin-features/src/features/teams/types/SelectedProjectType.ts +8 -0
  88. package/@danieli-automation/devops-plugin-features/src/features/teams/types/TeamMemberType.ts +7 -0
  89. package/@danieli-automation/devops-plugin-features/src/features/teams/types/TeamRowSeedType.ts +6 -0
  90. package/@danieli-automation/devops-plugin-features/src/features/teams/types/UserSearchResultType.ts +8 -0
  91. package/@danieli-automation/devops-plugin-features/src/features/work-items/api/states.ts +24 -0
  92. package/@danieli-automation/devops-plugin-features/src/features/work-items/api/wiql.ts +146 -0
  93. package/@danieli-automation/devops-plugin-features/src/features/work-items/api/workItems.ts +193 -0
  94. package/@danieli-automation/devops-plugin-features/src/features/work-items/constants/DefaultConsant.ts +43 -0
  95. package/@danieli-automation/devops-plugin-features/src/features/work-items/constants/FieldConstant.ts +27 -0
  96. package/@danieli-automation/devops-plugin-features/src/features/work-items/constants/StateConstant.ts +16 -0
  97. package/@danieli-automation/devops-plugin-features/src/features/work-items/constants/TypeConstant.ts +8 -0
  98. package/@danieli-automation/devops-plugin-features/src/features/work-items/services/WIQLService.ts +101 -0
  99. package/@danieli-automation/devops-plugin-features/src/features/work-items/services/WorkItemService.ts +54 -0
  100. package/@danieli-automation/devops-plugin-features/src/features/work-items/types/AdoWorkItemType.ts +18 -0
  101. package/@danieli-automation/devops-plugin-features/src/features/work-items/utils/__tests__/filter.test.ts +87 -0
  102. package/@danieli-automation/devops-plugin-features/src/features/work-items/utils/__tests__/workItem.test.ts +116 -0
  103. package/@danieli-automation/devops-plugin-features/src/features/work-items/utils/filter.ts +189 -0
  104. package/@danieli-automation/devops-plugin-features/src/features/work-items/utils/workItem.ts +245 -0
  105. package/@danieli-automation/devops-plugin-features/src/index.ts +0 -0
  106. package/@danieli-automation/devops-plugin-features/src/test/mocks/azure-devops-extension-api/Work.ts +5 -0
  107. package/@danieli-automation/devops-plugin-features/tsconfig.json +18 -0
  108. package/@danieli-automation/devops-plugin-features/vitest.config.ts +21 -0
  109. package/README.md +55 -0
  110. package/devops-plugin-kit-0.1.0.tgz +0 -0
  111. package/package.json +29 -0
  112. package/tsconfig.base.json +15 -0
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@danieli-automation/create-devops-plugin",
3
+ "version": "0.1.0",
4
+ "description": "Scaffold Azure DevOps plugin template and manifest files",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "bin": {
10
+ "create-devops-plugin": "dist/cli.js"
11
+ },
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js"
19
+ }
20
+ },
21
+ "scripts": {
22
+ "build": "tsc -p tsconfig.json && node -e \"require('node:fs').cpSync('src/template/static','dist/template/static',{recursive:true,force:true})\"",
23
+ "clean": "Remove-Item -Recurse -Force dist -ErrorAction SilentlyContinue",
24
+ "lint": "echo lint not configured",
25
+ "test": "echo test not configured"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ }
30
+ }
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env node
2
+
3
+ import path from "node:path";
4
+ import process from "node:process";
5
+ import { createPluginTemplate } from "./index.js";
6
+
7
+ type CliArgs = {
8
+ pluginName: string;
9
+ publisher: string;
10
+ targetDir: string;
11
+ extensionId?: string;
12
+ installDependencies: boolean;
13
+ force: boolean;
14
+ };
15
+
16
+ /**
17
+ * Parses command line flags and positional arguments for scaffold execution.
18
+ *
19
+ * @param argv - raw process argv list
20
+ * @throws Error when required options are missing or malformed
21
+ */
22
+ function parseArgs(argv: string[]): CliArgs {
23
+ const args = [...argv];
24
+ const pluginName = args.shift();
25
+ if (!pluginName) {
26
+ throw new Error("Missing required argument: <plugin-name>");
27
+ }
28
+
29
+ let publisher = "";
30
+ let targetDir = process.cwd();
31
+ let extensionId: string | undefined;
32
+ let installDependencies = true;
33
+ let force = false;
34
+
35
+ while (args.length > 0) {
36
+ const token = args.shift();
37
+
38
+ if (!token) {
39
+ continue;
40
+ }
41
+
42
+ if (token === "--publisher" || token === "-p") {
43
+ publisher = args.shift() ?? "";
44
+ continue;
45
+ }
46
+
47
+ if (token === "--targetDir" || token === "--target-dir" || token === "-t") {
48
+ targetDir = args.shift() ?? targetDir;
49
+ continue;
50
+ }
51
+
52
+ if (token === "--extensionId" || token === "--extension-id" || token === "-e") {
53
+ extensionId = args.shift();
54
+ continue;
55
+ }
56
+
57
+ if (token === "--no-install") {
58
+ installDependencies = false;
59
+ continue;
60
+ }
61
+
62
+ if (token === "--force" || token === "-f") {
63
+ force = true;
64
+ continue;
65
+ }
66
+
67
+ if (token === "--help" || token === "-h") {
68
+ printHelp();
69
+ process.exit(0);
70
+ }
71
+
72
+ throw new Error(`Unknown argument: ${token}`);
73
+ }
74
+
75
+ if (!publisher) {
76
+ throw new Error("Missing required option: --publisher <publisher-id>");
77
+ }
78
+
79
+ return {
80
+ pluginName,
81
+ publisher,
82
+ targetDir,
83
+ extensionId,
84
+ installDependencies,
85
+ force
86
+ };
87
+ }
88
+
89
+ /**
90
+ * Prints usage help for the create-devops-plugin CLI.
91
+ */
92
+ function printHelp() {
93
+ console.log(
94
+ [
95
+ "Usage:",
96
+ " create-devops-plugin <plugin-name> --publisher <publisher-id> [options]",
97
+ "",
98
+ "Options:",
99
+ " -p, --publisher <id> Azure DevOps publisher id (required)",
100
+ " -t, --target-dir <path> Parent folder (default: current directory)",
101
+ " -e, --extension-id <id> Manifest extension id override",
102
+ " --no-install Skip npm install in generated plugin",
103
+ " -f, --force Allow non-empty target directory",
104
+ " -h, --help Show help"
105
+ ].join("\n")
106
+ );
107
+ }
108
+
109
+ async function main() {
110
+ try {
111
+ const argv = process.argv.slice(2);
112
+ if (argv.includes("--help") || argv.includes("-h")) {
113
+ printHelp();
114
+ return;
115
+ }
116
+
117
+ const parsed = parseArgs(argv);
118
+ const outputDir = await createPluginTemplate({
119
+ pluginName: parsed.pluginName,
120
+ publisher: parsed.publisher,
121
+ targetDir: path.resolve(parsed.targetDir),
122
+ extensionId: parsed.extensionId,
123
+ installDependencies: parsed.installDependencies,
124
+ force: parsed.force
125
+ });
126
+
127
+ console.log(`Plugin created at: ${outputDir}`);
128
+ } catch (error) {
129
+ const message = error instanceof Error ? error.message : String(error);
130
+ console.error(`create-devops-plugin failed: ${message}`);
131
+ printHelp();
132
+ process.exit(1);
133
+ }
134
+ }
135
+
136
+ void main();
@@ -0,0 +1,37 @@
1
+ import path from "node:path";
2
+ import { createManifests } from "./template/manifest.js";
3
+ import { runNpmInstall } from "./template/npm.js";
4
+ import { createTemplateFiles } from "./template/files/index.js";
5
+ import { copyStaticFolder, ensureOutputDir, writeFiles } from "./template/fs.js";
6
+ import { normalizeId } from "./template/utils.js";
7
+ import type { CreatePluginTemplateInput } from "./template/types.js";
8
+
9
+ export type { CreatePluginTemplateInput } from "./template/types.js";
10
+
11
+ /**
12
+ * Creates a development-ready Azure DevOps plugin project from templates.
13
+ *
14
+ * @param input - scaffold options for target path, naming, and install behavior
15
+ * @throws Error if directory validation, file generation, or dependency install fails
16
+ */
17
+ export async function createPluginTemplate(input: CreatePluginTemplateInput) {
18
+ const context = {
19
+ pluginName: input.pluginName,
20
+ publisher: input.publisher,
21
+ extensionId: normalizeId(input.extensionId ?? input.pluginName)
22
+ };
23
+
24
+ const outputDir = path.resolve(input.targetDir, input.pluginName);
25
+ await ensureOutputDir(outputDir, Boolean(input.force));
26
+
27
+ const { manifest, devManifest } = createManifests(context);
28
+ const files = createTemplateFiles({ context, manifest, devManifest });
29
+ await writeFiles(outputDir, files);
30
+ await copyStaticFolder(outputDir);
31
+
32
+ if (input.installDependencies ?? true) {
33
+ await runNpmInstall(outputDir);
34
+ }
35
+
36
+ return outputDir;
37
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Generates the azure-devops-extension-api mock index template entry.
3
+ */
4
+ export function apiIndexMockFile(): [string, string] {
5
+ return [
6
+ "src/test/mocks/azure-devops-extension-api/index.ts",
7
+ `export {};
8
+ `
9
+ ];
10
+ }
@@ -0,0 +1,25 @@
1
+ import type { TemplateContext } from "../types.js";
2
+ import { escapeForHtml } from "../utils.js";
3
+
4
+ /**
5
+ * Generates the main application HTML template entry.
6
+ *
7
+ * @param context - template metadata for document title
8
+ */
9
+ export function appHtmlFile(context: TemplateContext): [string, string] {
10
+ return [
11
+ "src/app/App.html",
12
+ `<!doctype html>
13
+ <html lang="en">
14
+ <head>
15
+ <meta charset="UTF-8" />
16
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
17
+ <title>${escapeForHtml(context.pluginName)}</title>
18
+ </head>
19
+ <body>
20
+ <div id="app-root"></div>
21
+ </body>
22
+ </html>
23
+ `
24
+ ];
25
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Generates the base stylesheet template entry.
3
+ */
4
+ export function appStylesFile(): [string, string] {
5
+ return [
6
+ "src/app/styles.css",
7
+ `:root {
8
+ font-family: Segoe UI, Arial, sans-serif;
9
+ }
10
+
11
+ body {
12
+ margin: 0;
13
+ background: #f5f6f8;
14
+ color: #1f2328;
15
+ }
16
+
17
+ .app {
18
+ padding: 24px;
19
+ }
20
+ `
21
+ ];
22
+ }
@@ -0,0 +1,41 @@
1
+ import type { TemplateContext } from "../types.js";
2
+ import { escapeForHtml } from "../utils.js";
3
+
4
+ /**
5
+ * Generates the main React entry component template entry.
6
+ *
7
+ * @param context - template metadata for UI text
8
+ */
9
+ export function appTsxFile(context: TemplateContext): [string, string] {
10
+ return [
11
+ "src/app/App.tsx",
12
+ `import React from "react";
13
+ import { createRoot } from "react-dom/client";
14
+ import * as SDK from "azure-devops-extension-sdk";
15
+ import "./styles.css";
16
+
17
+ async function bootstrap() {
18
+ SDK.init();
19
+ await SDK.ready();
20
+
21
+ const root = document.getElementById("app-root");
22
+ if (!root) {
23
+ throw new Error("Root element not found");
24
+ }
25
+
26
+ createRoot(root).render(
27
+ <React.StrictMode>
28
+ <main className="app">
29
+ <h1>${escapeForHtml(context.pluginName)}</h1>
30
+ <p>Your Azure DevOps plugin template is ready.</p>
31
+ </main>
32
+ </React.StrictMode>
33
+ );
34
+ }
35
+
36
+ bootstrap().catch((error) => {
37
+ console.error("Bootstrap failed", error);
38
+ });
39
+ `
40
+ ];
41
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Generates the environment example file template entry.
3
+ */
4
+ export function envExampleFile(): [string, string] {
5
+ return [
6
+ ".env.example",
7
+ `ENV=dev
8
+ PORT=8080
9
+ SCOPE=User`
10
+ ];
11
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Generates the .gitignore template entry.
3
+ */
4
+ export function gitignoreFile(): [string, string] {
5
+ return [
6
+ ".gitignore",
7
+ `node_modules
8
+ dist
9
+ *.vsix
10
+ coverage
11
+ `
12
+ ];
13
+ }
@@ -0,0 +1,51 @@
1
+ import type { TemplateContext } from "../types.js";
2
+ import { apiIndexMockFile } from "./apiIndexMock.js";
3
+ import { appHtmlFile } from "./appHtml.js";
4
+ import { appStylesFile } from "./appStyles.js";
5
+ import { appTsxFile } from "./appTsx.js";
6
+ import { envExampleFile } from "./envExample.js";
7
+ import { gitignoreFile } from "./gitignore.js";
8
+ import { packageJsonFile } from "./packageJson.js";
9
+ import { readmeFile } from "./readme.js";
10
+ import { sdkMockFile } from "./sdkMock.js";
11
+ import { testSetupFile } from "./testSetup.js";
12
+ import { tsconfigJsonFile } from "./tsconfigJson.js";
13
+ import { vitestConfigFile } from "./vitestConfig.js";
14
+ import { webpackAppConfigFile } from "./webpackAppConfig.js";
15
+ import { webpackCommonConfigFile } from "./webpackCommonConfig.js";
16
+ import { webpackConfigFile } from "./webpackConfig.js";
17
+
18
+ type Props = {
19
+ context: TemplateContext;
20
+ manifest: Record<string, unknown>;
21
+ devManifest: Record<string, unknown>;
22
+ }
23
+
24
+ /**
25
+ * Builds the full file map for project scaffolding.
26
+ *
27
+ * @param context - template metadata used in generated files
28
+ * @param manifest - production manifest content
29
+ * @param devManifest - development manifest content
30
+ */
31
+ export function createTemplateFiles({ context, manifest, devManifest }: Props): Map<string, string> {
32
+ return new Map<string, string>([
33
+ packageJsonFile(context),
34
+ tsconfigJsonFile(),
35
+ webpackConfigFile(),
36
+ webpackCommonConfigFile(),
37
+ webpackAppConfigFile(),
38
+ vitestConfigFile(),
39
+ appTsxFile(context),
40
+ appHtmlFile(context),
41
+ appStylesFile(),
42
+ testSetupFile(),
43
+ sdkMockFile(),
44
+ apiIndexMockFile(),
45
+ envExampleFile(),
46
+ ["vss-extension.json", JSON.stringify(manifest, null, 2)],
47
+ ["vss-extension.dev.json", JSON.stringify(devManifest, null, 2)],
48
+ gitignoreFile(),
49
+ readmeFile(context)
50
+ ]);
51
+ }
@@ -0,0 +1,60 @@
1
+ import type { TemplateContext } from "../types.js";
2
+
3
+ /**
4
+ * Generates the package.json template entry.
5
+ *
6
+ * @param context - template metadata for package naming
7
+ */
8
+ export function packageJsonFile(context: TemplateContext): [string, string] {
9
+ return [
10
+ "package.json",
11
+ JSON.stringify(
12
+ {
13
+ name: context.extensionId,
14
+ version: "0.0.1",
15
+ private: true,
16
+ scripts: {
17
+ dev: "webpack serve --config webpack.config.cjs --env ENV=dev",
18
+ build: "webpack --config webpack.config.cjs --env ENV=prod",
19
+ test: "vitest run",
20
+ "test:watch": "vitest",
21
+ package: "npm run build && tfx extension create --manifest-globs vss-extension.json",
22
+ "package:dev": "tfx extension create --manifest-globs vss-extension.dev.json"
23
+ },
24
+ dependencies: {
25
+ react: "^19.1.0",
26
+ "react-dom": "^19.1.0",
27
+ "azure-devops-extension-sdk": "^4.0.2",
28
+ "azure-devops-extension-api": "^4.251.0"
29
+ },
30
+ devDependencies: {
31
+ "@types/node": "^24.3.0",
32
+ "@types/react": "^19.1.10",
33
+ "@types/react-dom": "^19.1.7",
34
+ "@vitest/coverage-v8": "^3.2.4",
35
+ "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
36
+ "copy-webpack-plugin": "^13.0.1",
37
+ dotenv: "^17.2.1",
38
+ "css-loader": "^7.1.2",
39
+ "fork-ts-checker-webpack-plugin": "^9.1.0",
40
+ jsdom: "^26.1.0",
41
+ "react-refresh": "^0.18.0",
42
+ "react-refresh-typescript": "^2.0.11",
43
+ sass: "^1.90.0",
44
+ "sass-loader": "^16.0.5",
45
+ "style-loader": "^4.0.0",
46
+ "ts-loader": "^9.5.2",
47
+ "tsconfig-paths-webpack-plugin": "^4.2.0",
48
+ typescript: "^5.9.2",
49
+ vitest: "^3.2.4",
50
+ webpack: "^5.101.0",
51
+ "webpack-cli": "^6.0.1",
52
+ "webpack-dev-server": "^5.2.2",
53
+ "tfx-cli": "^0.21.1"
54
+ }
55
+ },
56
+ null,
57
+ 2
58
+ )
59
+ ];
60
+ }
@@ -0,0 +1,30 @@
1
+ import type { TemplateContext } from "../types.js";
2
+
3
+ /**
4
+ * Generates the README template entry.
5
+ *
6
+ * @param context - template metadata for project title
7
+ */
8
+ export function readmeFile(context: TemplateContext): [string, string] {
9
+ return [
10
+ "README.md",
11
+ `# ${context.pluginName}
12
+
13
+ ## Commands
14
+
15
+ - \`npm run dev\`: start webpack dev server
16
+ - \`npm run build\`: production build to \`dist\`
17
+ - \`npm run test\`: run unit tests with vitest
18
+ - \`npm run test:watch\`: run tests in watch mode
19
+ - \`npm run package\`: build and create VSIX using \`vss-extension.json\`
20
+ - \`npm run package:dev\`: create VSIX using \`vss-extension.dev.json\`
21
+
22
+ ## Configuration Layout
23
+
24
+ - \`webpack.config.cjs\`: webpack entry point
25
+ - \`config/webpack/common.cjs\`: shared webpack rules and plugins
26
+ - \`config/webpack/app.cjs\`: app bundle configuration
27
+ - \`vitest.config.ts\`: test and coverage setup
28
+ `
29
+ ];
30
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Generates the azure-devops-extension-sdk mock template entry.
3
+ */
4
+ export function sdkMockFile(): [string, string] {
5
+ return [
6
+ "src/test/mocks/azure-devops-extension-sdk.ts",
7
+ `export const init = () => undefined;
8
+ export const ready = async () => undefined;
9
+ `
10
+ ];
11
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Generates the vitest setup template entry.
3
+ */
4
+ export function testSetupFile(): [string, string] {
5
+ return [
6
+ "src/test/setup.ts",
7
+ `import { vi } from "vitest";
8
+
9
+ vi.mock("azure-devops-extension-sdk", () => ({
10
+ default: {},
11
+ init: vi.fn(),
12
+ ready: vi.fn().mockResolvedValue(undefined)
13
+ }));
14
+ `
15
+ ];
16
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Generates the tsconfig.json template entry.
3
+ */
4
+ export function tsconfigJsonFile(): [string, string] {
5
+ return [
6
+ "tsconfig.json",
7
+ JSON.stringify(
8
+ {
9
+ compilerOptions: {
10
+ target: "ES2022",
11
+ module: "ESNext",
12
+ lib: ["ES2022", "DOM", "DOM.Iterable"],
13
+ jsx: "react-jsx",
14
+ moduleResolution: "Node",
15
+ strict: true,
16
+ skipLibCheck: true,
17
+ sourceMap: true,
18
+ baseUrl: ".",
19
+ paths: {
20
+ "core/*": ["src/core/*"],
21
+ "app/*": ["src/app/*"],
22
+ }
23
+ },
24
+ include: ["src", "vitest.config.ts"]
25
+ },
26
+ null,
27
+ 2
28
+ )
29
+ ];
30
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Generates the vitest.config.ts template entry.
3
+ */
4
+ export function vitestConfigFile(): [string, string] {
5
+ return [
6
+ "vitest.config.ts",
7
+ `import path from "node:path";
8
+ import { defineConfig } from "vitest/config";
9
+
10
+ export default defineConfig({
11
+ resolve: {
12
+ alias: [
13
+ {
14
+ find: "azure-devops-extension-sdk",
15
+ replacement: path.resolve(__dirname, "src/test/mocks/azure-devops-extension-sdk.ts")
16
+ },
17
+ {
18
+ find: /^azure-devops-extension-api\\/(.*)$/,
19
+ replacement: path.resolve(__dirname, "src/test/mocks/azure-devops-extension-api/$1.ts")
20
+ },
21
+ {
22
+ find: /^azure-devops-extension-api$/,
23
+ replacement: path.resolve(__dirname, "src/test/mocks/azure-devops-extension-api/index.ts")
24
+ },
25
+ { find: /^core\\/(.*)$/, replacement: path.resolve(__dirname, "src/core/$1") },
26
+ { find: /^app\\/(.*)$/, replacement: path.resolve(__dirname, "src/app/$1") },
27
+ { find: /^tabs\\/(.*)$/, replacement: path.resolve(__dirname, "src/app/tabs/$1") }
28
+ ]
29
+ },
30
+ test: {
31
+ environment: "jsdom",
32
+ setupFiles: ["./src/test/setup.ts"],
33
+ globals: true,
34
+ restoreMocks: true,
35
+ clearMocks: true,
36
+ mockReset: true,
37
+ coverage: {
38
+ all: true,
39
+ provider: "v8",
40
+ reporter: ["text", "html", "lcov"],
41
+ reportsDirectory: "./coverage",
42
+ include: ["src/**/*.ts", "src/**/*.tsx"],
43
+ exclude: [
44
+ "**/node_modules/**",
45
+ "**/dist/**",
46
+ "**/coverage/**",
47
+ "**/*.d.ts",
48
+ "src/test/**",
49
+ "src/**/__tests__/**",
50
+ "src/**/__mocks__/**"
51
+ ]
52
+ }
53
+ }
54
+ });
55
+ `
56
+ ];
57
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Generates the app webpack config template entry.
3
+ */
4
+ export function webpackAppConfigFile(): [string, string] {
5
+ return [
6
+ "config/webpack/app.cjs",
7
+ `const path = require("path");
8
+ const { createCommonConfig } = require("./common.cjs");
9
+
10
+ function createAppConfig(env) {
11
+ const isDev = env === "dev";
12
+
13
+ return createCommonConfig({
14
+ env,
15
+ name: "app",
16
+ entry: { app: "./src/app/App.tsx" },
17
+ outputPath: path.resolve(__dirname, "../../dist/app"),
18
+ publicPath: isDev ? "/app/" : "./",
19
+ withDevServer: true,
20
+ copyPatterns: [
21
+ { from: "src/app/App.html", to: "App.html" },
22
+ { from: "static/images", to: "static/images", noErrorOnMissing: true },
23
+ { from: "static/fonts", to: "static/fonts", noErrorOnMissing: true }
24
+ ]
25
+ });
26
+ }
27
+
28
+ module.exports = { createAppConfig };
29
+ `
30
+ ];
31
+ }