Creating a new database project, project items

* can create, open, and close sqlproj files
* can add sql objects to projects
This commit is contained in:
Benjin Dubishar
2020-04-17 14:09:59 -07:00
committed by GitHub
parent 05526bbaca
commit b3492e3f57
34 changed files with 1782 additions and 94 deletions

View File

@@ -4,39 +4,40 @@
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as xml2js from 'xml2js';
import * as path from 'path';
import * as xmldom from 'xmldom';
import * as constants from '../common/constants';
import { promises as fs } from 'fs';
import { DataSource } from './dataSources/dataSources';
import { getErrorMessage } from '../common/utils';
/**
* Class representing a Project, and providing functions for operating on it
*/
export class Project {
public projectFile: string;
public projectFilePath: string;
public files: ProjectEntry[] = [];
public dataSources: DataSource[] = [];
public get projectFolderPath() {
return path.dirname(this.projectFilePath);
}
private projFileXmlDoc: any = undefined;
constructor(projectFilePath: string) {
this.projectFile = projectFilePath;
this.projectFilePath = projectFilePath;
}
/**
* Reads the project setting and contents from the file
*/
public async readProjFile() {
let projFileContents = await fs.readFile(this.projectFile);
const parser = new xml2js.Parser({
explicitArray: true,
explicitCharkey: false,
explicitRoot: false
});
let result;
const projFileText = await fs.readFile(this.projectFilePath);
try {
result = await parser.parseStringPromise(projFileContents.toString());
this.projFileXmlDoc = new xmldom.DOMParser().parseFromString(projFileText.toString());
}
catch (err) {
vscode.window.showErrorMessage(err);
@@ -45,23 +46,115 @@ export class Project {
// find all folders and files to include
for (const itemGroup of result['ItemGroup']) {
if (itemGroup['Build'] !== undefined) {
for (const fileEntry of itemGroup['Build']) {
this.files.push(this.createProjectEntry(fileEntry.$['Include'], EntryType.File));
}
for (let ig = 0; ig < this.projFileXmlDoc.documentElement.getElementsByTagName(constants.ItemGroup).length; ig++) {
const itemGroup = this.projFileXmlDoc.documentElement.getElementsByTagName(constants.ItemGroup)[ig];
for (let b = 0; b < itemGroup.getElementsByTagName(constants.Build).length; b++) {
this.files.push(this.createProjectEntry(itemGroup.getElementsByTagName(constants.Build)[b].getAttribute(constants.Include), EntryType.File));
}
if (itemGroup['Folder'] !== undefined) {
for (const folderEntry of itemGroup['Folder']) {
this.files.push(this.createProjectEntry(folderEntry.$['Include'], EntryType.Folder));
}
for (let f = 0; f < itemGroup.getElementsByTagName(constants.Folder).length; f++) {
this.files.push(this.createProjectEntry(itemGroup.getElementsByTagName(constants.Folder)[f].getAttribute(constants.Include), EntryType.Folder));
}
}
}
/**
* Adds a folder to the project, and saves the project file
* @param relativeFolderPath Relative path of the folder
*/
public async addFolderItem(relativeFolderPath: string): Promise<ProjectEntry> {
const absoluteFolderPath = path.join(this.projectFolderPath, relativeFolderPath);
await fs.mkdir(absoluteFolderPath, { recursive: true });
const folderEntry = this.createProjectEntry(relativeFolderPath, EntryType.Folder);
this.files.push(folderEntry);
await this.addToProjFile(folderEntry);
return folderEntry;
}
/**
* Writes a file to disk, adds that file to the project, and writes it to disk
* @param relativeFilePath Relative path of the file
* @param contents Contents to be written to the new file
*/
public async addScriptItem(relativeFilePath: string, contents: string): Promise<ProjectEntry> {
const absoluteFilePath = path.join(this.projectFolderPath, relativeFilePath);
await fs.mkdir(path.dirname(absoluteFilePath), { recursive: true });
await fs.writeFile(absoluteFilePath, contents);
const fileEntry = this.createProjectEntry(relativeFilePath, EntryType.File);
this.files.push(fileEntry);
await this.addToProjFile(fileEntry);
return fileEntry;
}
private createProjectEntry(relativePath: string, entryType: EntryType): ProjectEntry {
return new ProjectEntry(vscode.Uri.file(path.join(this.projectFile, relativePath)), entryType);
return new ProjectEntry(vscode.Uri.file(path.join(this.projectFolderPath, relativePath)), relativePath, entryType);
}
private findOrCreateItemGroup(containedTag?: string): any {
let outputItemGroup = undefined;
// find any ItemGroup node that contains files; that's where we'll add
for (let i = 0; i < this.projFileXmlDoc.documentElement.getElementsByTagName(constants.ItemGroup).length; i++) {
const currentItemGroup = this.projFileXmlDoc.documentElement.getElementsByTagName(constants.ItemGroup)[i];
// if we're not hunting for a particular child type, or if we are and we find it, use the ItemGroup
if (!containedTag || currentItemGroup.getElementsByTagName(containedTag).length > 0) {
outputItemGroup = currentItemGroup;
break;
}
}
// if none already exist, make a new ItemGroup for it
if (!outputItemGroup) {
outputItemGroup = this.projFileXmlDoc.createElement(constants.ItemGroup);
this.projFileXmlDoc.documentElement.appendChild(outputItemGroup);
}
return outputItemGroup;
}
private addFileToProjFile(path: string) {
const newFileNode = this.projFileXmlDoc.createElement(constants.Build);
newFileNode.setAttribute(constants.Include, path);
this.findOrCreateItemGroup(constants.Build).appendChild(newFileNode);
}
private addFolderToProjFile(path: string) {
const newFolderNode = this.projFileXmlDoc.createElement(constants.Folder);
newFolderNode.setAttribute(constants.Include, path);
this.findOrCreateItemGroup(constants.Folder).appendChild(newFolderNode);
}
private async addToProjFile(entry: ProjectEntry) {
try {
switch (entry.type) {
case EntryType.File:
this.addFileToProjFile(entry.relativePath);
break;
case EntryType.Folder:
this.addFolderToProjFile(entry.relativePath);
}
await this.serializeToProjFile(this.projFileXmlDoc);
}
catch (err) {
vscode.window.showErrorMessage(getErrorMessage(err));
return;
}
}
private async serializeToProjFile(projFileContents: any) {
const xml = new xmldom.XMLSerializer().serializeToString(projFileContents); // TODO: how to get this to serialize with "pretty" formatting
await fs.writeFile(this.projectFilePath, xml);
}
}
@@ -72,16 +165,18 @@ export class ProjectEntry {
/**
* Absolute file system URI
*/
uri: vscode.Uri;
fsUri: vscode.Uri;
relativePath: string;
type: EntryType;
constructor(uri: vscode.Uri, type: EntryType) {
this.uri = uri;
constructor(uri: vscode.Uri, relativePath: string, type: EntryType) {
this.fsUri = uri;
this.relativePath = relativePath;
this.type = type;
}
public toString(): string {
return this.uri.path;
return this.fsUri.path;
}
}