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.
- package/@danieli-automation/create-devops-plugin/package.json +30 -0
- package/@danieli-automation/create-devops-plugin/src/cli.ts +136 -0
- package/@danieli-automation/create-devops-plugin/src/index.ts +37 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/apiIndexMock.ts +10 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/appHtml.ts +25 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/appStyles.ts +22 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/appTsx.ts +41 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/envExample.ts +11 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/gitignore.ts +13 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/index.ts +51 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/packageJson.ts +60 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/readme.ts +30 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/sdkMock.ts +11 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/testSetup.ts +16 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/tsconfigJson.ts +30 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/vitestConfig.ts +57 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/webpackAppConfig.ts +31 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/webpackCommonConfig.ts +116 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/webpackConfig.ts +15 -0
- package/@danieli-automation/create-devops-plugin/src/template/files.ts +1 -0
- package/@danieli-automation/create-devops-plugin/src/template/fs.ts +85 -0
- package/@danieli-automation/create-devops-plugin/src/template/manifest.ts +40 -0
- package/@danieli-automation/create-devops-plugin/src/template/npm.ts +22 -0
- package/@danieli-automation/create-devops-plugin/src/template/static/fonts/AzDevMDL2.woff +0 -0
- package/@danieli-automation/create-devops-plugin/src/template/static/fonts/bowtie.woff2 +0 -0
- package/@danieli-automation/create-devops-plugin/src/template/static/fonts/fabric-icons.woff +0 -0
- package/@danieli-automation/create-devops-plugin/src/template/static/fonts/fluent-filled-v1.1.293.woff2 +0 -0
- package/@danieli-automation/create-devops-plugin/src/template/static/fonts/fluent-regular-v1.1.293.woff2 +0 -0
- package/@danieli-automation/create-devops-plugin/src/template/static/images/DigiMetLogo.jpeg +0 -0
- package/@danieli-automation/create-devops-plugin/src/template/static/images/danieliAutomation.png +0 -0
- package/@danieli-automation/create-devops-plugin/src/template/static/images/danieliAutomationBlack.jpg +0 -0
- package/@danieli-automation/create-devops-plugin/src/template/static/images/danieli_digi_met_logo.jpeg +0 -0
- package/@danieli-automation/create-devops-plugin/src/template/static/images/logoSmallpng.png +0 -0
- package/@danieli-automation/create-devops-plugin/src/template/types.ts +14 -0
- package/@danieli-automation/create-devops-plugin/src/template/utils.ts +22 -0
- package/@danieli-automation/create-devops-plugin/tsconfig.json +8 -0
- package/@danieli-automation/devops-plugin-core/package.json +27 -0
- package/@danieli-automation/devops-plugin-core/src/core/azureClients.ts +18 -0
- package/@danieli-automation/devops-plugin-core/src/core/storage/createStore.ts +65 -0
- package/@danieli-automation/devops-plugin-core/src/core/storage/hooks/useCrossTeamSprintInstance.ts +145 -0
- package/@danieli-automation/devops-plugin-core/src/core/storage/hooks/useTaskOrder.ts +125 -0
- package/@danieli-automation/devops-plugin-core/src/core/storage/hooks/useWorkItemOrder.ts +86 -0
- package/@danieli-automation/devops-plugin-core/src/core/storage/index.ts +13 -0
- package/@danieli-automation/devops-plugin-core/src/core/storage/keys.ts +31 -0
- package/@danieli-automation/devops-plugin-core/src/core/storage/repositories/instance.ts +184 -0
- package/@danieli-automation/devops-plugin-core/src/core/storage/repositories/taskOrder.ts +59 -0
- package/@danieli-automation/devops-plugin-core/src/core/storage/repositories/workItemOrder.ts +60 -0
- package/@danieli-automation/devops-plugin-core/src/core/storage/stores.ts +18 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/AdoWorkItemType.ts +18 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/KVStoreType.ts +1 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/ScopeType.ts +1 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/SelectedProjectType.ts +8 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/SortConfigType.ts +2 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/instance/CreateInstanceInputType.ts +10 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/instance/CrossSprintInstanceType.ts +20 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/instance/DefaultInstanceType.ts +12 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/instance/InstanceRowType.ts +18 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/instance/UpdateInstanceInputType.ts +10 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/taskOrder/TaskOrderMapType.ts +3 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/taskOrder/TaskOrderType.ts +1 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/workItemOrder/WorkItemOrderMapType.ts +3 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/workItemOrder/WorkItemOrderType.ts +1 -0
- package/@danieli-automation/devops-plugin-core/src/index.ts +1 -0
- package/@danieli-automation/devops-plugin-core/src/pluginCore.ts +12 -0
- package/@danieli-automation/devops-plugin-core/tsconfig.json +16 -0
- package/@danieli-automation/devops-plugin-features/package.json +31 -0
- package/@danieli-automation/devops-plugin-features/src/app/stores/useUIStore.ts +12 -0
- package/@danieli-automation/devops-plugin-features/src/app/utils/date.ts +9 -0
- package/@danieli-automation/devops-plugin-features/src/app/utils/global.ts +9 -0
- package/@danieli-automation/devops-plugin-features/src/core/azureClients.ts +12 -0
- package/@danieli-automation/devops-plugin-features/src/features/instances/constants/InstanceConstant.ts +5 -0
- package/@danieli-automation/devops-plugin-features/src/features/instances/hooks/useInstancePermission.ts +127 -0
- package/@danieli-automation/devops-plugin-features/src/features/instances/stores/__tests__/useInstanceStore.test.ts +25 -0
- package/@danieli-automation/devops-plugin-features/src/features/instances/stores/types/InstanceStoreType.ts +7 -0
- package/@danieli-automation/devops-plugin-features/src/features/instances/stores/useInstanceStore.ts +9 -0
- package/@danieli-automation/devops-plugin-features/src/features/instances/types/CrossSprintInstanceType.ts +20 -0
- package/@danieli-automation/devops-plugin-features/src/features/instances/utils/instance.ts +55 -0
- package/@danieli-automation/devops-plugin-features/src/features/iterations/api/iterations.ts +298 -0
- package/@danieli-automation/devops-plugin-features/src/features/iterations/services/IterationService.ts +215 -0
- package/@danieli-automation/devops-plugin-features/src/features/iterations/types/IterationInfoType.ts +15 -0
- package/@danieli-automation/devops-plugin-features/src/features/iterations/utils/__tests__/iteration.test.ts +39 -0
- package/@danieli-automation/devops-plugin-features/src/features/iterations/utils/iteration.ts +132 -0
- package/@danieli-automation/devops-plugin-features/src/features/teams/api/projects.ts +79 -0
- package/@danieli-automation/devops-plugin-features/src/features/teams/api/teams.ts +29 -0
- package/@danieli-automation/devops-plugin-features/src/features/teams/api/users.ts +124 -0
- package/@danieli-automation/devops-plugin-features/src/features/teams/services/ProjectService.ts +80 -0
- package/@danieli-automation/devops-plugin-features/src/features/teams/types/SelectedProjectType.ts +8 -0
- package/@danieli-automation/devops-plugin-features/src/features/teams/types/TeamMemberType.ts +7 -0
- package/@danieli-automation/devops-plugin-features/src/features/teams/types/TeamRowSeedType.ts +6 -0
- package/@danieli-automation/devops-plugin-features/src/features/teams/types/UserSearchResultType.ts +8 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/api/states.ts +24 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/api/wiql.ts +146 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/api/workItems.ts +193 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/constants/DefaultConsant.ts +43 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/constants/FieldConstant.ts +27 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/constants/StateConstant.ts +16 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/constants/TypeConstant.ts +8 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/services/WIQLService.ts +101 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/services/WorkItemService.ts +54 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/types/AdoWorkItemType.ts +18 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/utils/__tests__/filter.test.ts +87 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/utils/__tests__/workItem.test.ts +116 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/utils/filter.ts +189 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/utils/workItem.ts +245 -0
- package/@danieli-automation/devops-plugin-features/src/index.ts +0 -0
- package/@danieli-automation/devops-plugin-features/src/test/mocks/azure-devops-extension-api/Work.ts +5 -0
- package/@danieli-automation/devops-plugin-features/tsconfig.json +18 -0
- package/@danieli-automation/devops-plugin-features/vitest.config.ts +21 -0
- package/README.md +55 -0
- package/devops-plugin-kit-0.1.0.tgz +0 -0
- package/package.json +29 -0
- 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,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,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
|
+
}
|