Skip to content

Commit f2bc791

Browse files
authored
feat: support user defined templates (#81)
1 parent 14313c3 commit f2bc791

10 files changed

+36
-30
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"jsonValidation": [
4141
{
4242
"fileMatch": "*.json",
43-
"url": "./resources/templates/emptySchema.json"
43+
"url": "./resources/jsonSchema.json"
4444
}
4545
],
4646
"languages": [
File renamed without changes.

src/common/constants.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@ export class Constants {
99
public static readonly UTF8 = "utf8";
1010
public static readonly EMPTY_STRING = "";
1111
public static readonly JSON_SPACE = 2;
12-
public static readonly RESOURCE_FOLDER = "resources";
1312
public static readonly TEMPLATE_FOLDER = "templates";
14-
public static readonly SAMPLE_FILE_NAME = "sample";
13+
public static readonly TEMPLATE_FILE_GLOB = "**/*.json";
1514
public static readonly DTDL_LANGUAGE_SERVER_ID = "dtdl-language-server";
1615
public static readonly DTDL_LANGUAGE_SERVER_NAME = "DTDL Language Server";
1716
public static readonly DTDL_LANGUAGE_SERVER_RELATIVE_PATH = "dist/main.js";

src/common/utility.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

4+
import * as glob from "glob";
45
import * as fs from "fs-extra";
56
import * as path from "path";
67
import { DeviceModelManager, ModelType } from "../deviceModel/deviceModelManager";
@@ -48,12 +49,11 @@ export class Utility {
4849
}
4950

5051
/**
51-
* get json content from file
52-
* @param filePath file path
52+
* list file in folder
53+
* @param folder folder path
5354
*/
54-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
55-
public static async getJsonContent(filePath: string): Promise<any> {
56-
return fs.readJson(filePath, { encoding: Constants.UTF8 });
55+
public static listFile(folder: string, filePattern: string): string[] {
56+
return glob.sync(filePattern, { cwd: folder });
5757
}
5858

5959
/**

src/deviceModel/deviceModelManager.ts

+7-16
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export class DeviceModelManager {
2626
* @param name model name
2727
*/
2828
public static generateModelId(name: string): string {
29-
return `dtmi:{company}:${name};1`;
29+
return `dtmi:com:example:${name};1`;
3030
}
3131

3232
/**
@@ -38,14 +38,6 @@ export class DeviceModelManager {
3838
return `${name}.json`;
3939
}
4040

41-
/**
42-
* get DigitalTwin template file name
43-
* @param type model type
44-
*/
45-
public static getTemplateFileName(): string {
46-
return DeviceModelManager.generateModelFileName(Constants.SAMPLE_FILE_NAME);
47-
}
48-
4941
private readonly component: string;
5042
constructor(private readonly context: vscode.ExtensionContext, private readonly outputChannel: ColorizedChannel) {
5143
this.component = Constants.DEVICE_MODEL_COMPONENT;
@@ -58,12 +50,14 @@ export class DeviceModelManager {
5850
public async createModel(type: ModelType): Promise<void> {
5951
const folder: string = await UI.selectRootFolder(UIConstants.SELECT_ROOT_FOLDER_LABEL);
6052
const name: string = await UI.inputModelName(UIConstants.INPUT_MODEL_NAME_LABEL, type, folder);
61-
const operation = `Create ${type} ${name} in folder ${folder}`;
53+
const templateFolder: string = this.context.asAbsolutePath(path.join(Constants.TEMPLATE_FOLDER));
54+
const template: string = await UI.selectTemplateFile(UIConstants.SELECT_TEMPLATE_FILE_LABEL, templateFolder);
55+
const operation = `Create ${type} "${name}" in folder ${folder} by template "${template}"`;
6256
this.outputChannel.start(operation, this.component);
6357

6458
let filePath: string;
6559
try {
66-
filePath = await this.doCreateModel(folder, name);
60+
filePath = await this.doCreateModel(folder, name, path.join(templateFolder, template));
6761
} catch (error) {
6862
throw new ProcessError(operation, error, this.component);
6963
}
@@ -75,16 +69,13 @@ export class DeviceModelManager {
7569

7670
/**
7771
* create DigitalTwin model
78-
* @param type model type
7972
* @param folder root folder
8073
* @param name model name
74+
* @param templatePath template file path
8175
*/
82-
private async doCreateModel(folder: string, name: string): Promise<string> {
76+
private async doCreateModel(folder: string, name: string, templatePath: string): Promise<string> {
8377
const modelId: string = DeviceModelManager.generateModelId(name);
8478
const filePath: string = path.join(folder, DeviceModelManager.generateModelFileName(name));
85-
const templatePath: string = this.context.asAbsolutePath(
86-
path.join(Constants.RESOURCE_FOLDER, Constants.TEMPLATE_FOLDER, DeviceModelManager.getTemplateFileName())
87-
);
8879
const replacement = new Map<string, string>();
8980
replacement.set(Constants.MODEL_ID_PLACEHOLDER, modelId);
9081
replacement.set(Constants.MODEL_NAME_PLACEHOLDER, name);

src/test/deviceModelManager.test.ts

+2
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ jest.mock("../view/ui");
1818

1919
describe("Device model manager", () => {
2020
const folder = "root";
21+
const template = "template";
2122
const context = vscode.ExtensionContext;
2223
const channel = new ColorizedChannel(Constants.CHANNEL_NAME);
2324
const manager = new DeviceModelManager(context, channel);
2425

2526
UI.selectRootFolder = jest.fn().mockResolvedValue(folder);
2627
UI.inputModelName = jest.fn().mockResolvedValue("test");
28+
UI.selectTemplateFile = jest.fn().mockResolvedValue(template);
2729

2830
test("create interface successfully", async () => {
2931
await manager.createModel(ModelType.Interface);

src/test/example.test.ts

-6
This file was deleted.

src/view/ui.ts

+19
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,25 @@ export class UI {
7878
return selected.description || (await UI.showOpenDialog(label));
7979
}
8080

81+
/**
82+
* select template file
83+
* @param label label
84+
* @param folder template folder
85+
*/
86+
public static async selectTemplateFile(label: string, folder: string): Promise<string> {
87+
const files: string[] = Utility.listFile(folder, Constants.TEMPLATE_FILE_GLOB);
88+
if (files.length === 1) {
89+
return files[0];
90+
}
91+
const items: vscode.QuickPickItem[] = files.map(file => {
92+
return {
93+
label: file
94+
};
95+
});
96+
const selected: vscode.QuickPickItem = await UI.showQuickPick(label, items);
97+
return selected.label;
98+
}
99+
81100
/**
82101
* input model name and validate
83102
* @param label label

src/view/uiConstants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
export class UIConstants {
88
public static readonly SELECT_ROOT_FOLDER_LABEL = "Select folder";
9+
public static readonly SELECT_TEMPLATE_FILE_LABEL = "Select template file";
910
public static readonly INPUT_MODEL_NAME_LABEL = "Input device model name";
1011
public static readonly BROWSE_LABEL = "Browse...";
1112
}
File renamed without changes.

0 commit comments

Comments
 (0)