Skip to content
This repository was archived by the owner on Nov 13, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"displayName": "PowerShell Universal",
"description": "Visual Studio Code tools for PowerShell Universal",
"publisher": "ironmansoftware",
"version": "5.5.1",
"version": "5.6.0",
"engines": {
"vscode": "^1.72.0"
},
Expand Down Expand Up @@ -147,9 +147,18 @@
},
{
"command": "powershell-universal.startUniversal",
"title": "PowerShell Universal: Start",
"title": "PowerShell Universal: Start Local Development",
"icon": "$(play)"
},
{
"command": "powershell-universal.clearLocalDatabase",
"title": "PowerShell Universal: Clear Local Development Database",
"icon": "$(delete)"
},
{
"command": "powershell-universal.connectLocalDevModule",
"title": "PowerShell Universal: Connect Local Development Module"
},
{
"command": "powershell-universal.manageDashboards",
"title": "PowerShell Universal: Manage Apps",
Expand Down Expand Up @@ -624,4 +633,4 @@
"temp": "^0.9.1",
"yaml": "^1.10.0"
}
}
}
9 changes: 9 additions & 0 deletions src/Universal.VSCode.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,13 @@ function Install-UniversalModule {
Install-Module @Parameters -Scope CurrentUser -Force -AllowClobber -ErrorAction SilentlyContinue
Import-Module @Parameters -ErrorAction SilentlyContinue
}
}

function Import-LocalDevelopmentModule {
param($Version, $Port)

$ModulePath = [IO.Path]::Combine($ENV:USERPROFILE, ".psu", $Version, "Modules", "Universal", "Universal.psd1")
Import-Module $ModulePath

Connect-PSUServer -Url "http://localhost:$Port" -Credential (New-Object System.Management.Automation.PSCredential("admin", (ConvertTo-SecureString "admin" -AsPlainText -Force)))
}
70 changes: 0 additions & 70 deletions src/commands/downloadUniversal.ts

This file was deleted.

174 changes: 174 additions & 0 deletions src/commands/localDev.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import * as vscode from 'vscode';
import { LocalDevConfig } from '../types';
import { Container } from '../container';
const os = require('os');
const https = require('https');
const fs = require('fs');
const temp = require('temp');
var AdmZip = require('adm-zip');
const path = require('path');

export const registerLocalDevCommands = (context: vscode.ExtensionContext) => {
context.subscriptions.push(vscode.commands.registerCommand('powershell-universal.downloadUniversal', downloadUniversal));
context.subscriptions.push(vscode.commands.registerCommand('powershell-universal.startUniversal', () => startPowerShellUniversal(context)));
context.subscriptions.push(vscode.commands.registerCommand('powershell-universal.clearLocalDatabase', clearLocalDatabase));
context.subscriptions.push(vscode.commands.registerCommand('powershell-universal.connectLocalDevModule', () => connectModule(context)));
};

const startPowerShellUniversal = async (context: vscode.ExtensionContext) => {
const config = getPsuDevConfig();
if (!config) {
return;
}

const psuPath = path.join(process.env.USERPROFILE, ".psu");
if (!fs.existsSync(psuPath)) {
await downloadUniversal();
}

let exe = 'Universal.Server.exe';
if (os.platform() === 'win32') {
exe = 'Universal.Server.exe';
} else {
exe = 'Universal.Server';
}

const universalPath = path.join(psuPath, config.version, exe);

if (vscode.workspace.workspaceFolders === undefined) {
vscode.window.showErrorMessage("No workspace folder is open. Please open a workspace folder and define a psu.dev.config to download Universal.");
return;
}

config.databaseType = config.databaseType || "SQLite";
let connectionString = config.databaseConnectionString || null;
if (connectionString === null) {
const databasePath = path.join(process.env.USERPROFILE, ".psu", "databases", vscode.workspace.name, "psu.db");
connectionString = `Data Source=${databasePath}`;
}

vscode.window.createTerminal({
name: `PowerShell Universal ${config.version}`,
shellPath: universalPath,
shellArgs: ["Mode", "Dev"],
env: {
"Data__RepositoryPath": vscode.workspace.workspaceFolders[0].uri.fsPath,
"Data__ConnectionString": connectionString,
"Plugin__0": config.databaseType,
"PSUDefaultAdminPassword": "admin",
"PSUDefaultAdminName": "admin",
...config.env,
}
}).show();

if (config.browserPort) {
vscode.env.openExternal(vscode.Uri.parse(`http://localhost:${config.browserPort || 5000}`));
}

context.globalState.update("psu.dev.config", config);

context.globalState.update("universal.connection", "Local Development");
vscode.commands.executeCommand('powershell-universal.refreshAllTreeViews');
};

const clearLocalDatabase = () => {
const dbPath = path.join(process.env.USERPROFILE, ".psu", "databases", vscode.workspace.name, "psu.db");
if (fs.existsSync(dbPath)) {
fs.unlinkSync(dbPath);

vscode.window.showInformationMessage(`Local development database cleared at.${dbPath}`);
}
};

const downloadUniversal = async () => {
vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: "Downloading Universal"
}, async (progress) => {
temp.track();

let platform = '';
switch (os.platform()) {
case 'darwin':
platform = 'osx';
break;
case 'linux':
platform = 'linux';
break;
case 'win32':
platform = 'win';
break;
default:
vscode.window.showErrorMessage("Unsupported platform");
return;
}

const config = getPsuDevConfig();
if (!config) {
return;
}

progress.report({ increment: 75, message: `Extracting PowerShell Universal ${config.version}` });

var tempPath = path.join(temp.dir, `Universal.${platform}-x64.${config.version}.zip`);
await downloadFile(`https://imsreleases.blob.core.windows.net/universal/production/${config.version}/Universal.${platform}-x64.${config.version}.zip`, tempPath);

const universalPath = path.join(process.env.USERPROFILE, ".psu", config.version);

var zip = new AdmZip(tempPath);
zip.extractAllTo(universalPath, true);

progress.report({ increment: 100, message: `PowerShell Universal ${config.version} downloaded and extracted to ${universalPath}` });
});
};

const downloadFile = (url: string, dest: string) => {
return new Promise((resolve, reject) => {
const file = fs.createWrite(dest);
file.on('finish', () => {
file.close(resolve);
});

file.on('error', (err: any) => {
fs.unlink(dest, () => reject(err));
});

https.get(url, (response: any) => {
if (response.statusCode !== 200) {
return reject(new Error(`Failed to download file: ${response.statusCode}`));
}
response.pipe(file);
}).on('error', (err: any) => {
fs.unlink(dest, () => reject(err));
});
});
};

const getPsuDevConfig = (): LocalDevConfig | null => {
if (vscode.workspace.workspaceFolders === undefined) {
vscode.window.showErrorMessage("No workspace folder is open. Please open a workspace folder and define a psu.dev.config to download Universal.");
return null;
}

const devDevPath = path.join(vscode.workspace.workspaceFolders[0].uri.fsPath, "psu.dev.json");
if (!fs.existsSync(devDevPath)) {
vscode.window.showErrorMessage("No psu.dev.json file found in the workspace. Please create one to start PowerShell Universal.");
return null;
}

const devJson = fs.readFileSync(devDevPath, 'utf8');
return JSON.parse(devJson);
};

const connectModule = async (context: vscode.ExtensionContext) => {

const config = getPsuDevConfig();

if (!config) {
vscode.window.showErrorMessage("No psu.dev.json file found in the workspace. Please create one to connect to PowerShell Universal.");
return;
}

Container.universal.sendTerminalCommand(`Import-Module (Join-Path '${__dirname}' 'Universal.VSCode.psm1')`);
Container.universal.sendTerminalCommand(`Import-LocalDevelopmentModule -Version '${config.version}' -Port '${config.browserPort || 5000}'`);
}
14 changes: 12 additions & 2 deletions src/connection-treeview.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as vscode from 'vscode';
import { IConnection, load } from './settings';
import { LocalDevConfig } from './types';

export class ConnectionTreeViewProvider implements vscode.TreeDataProvider<vscode.TreeItem> {

Expand All @@ -16,7 +17,7 @@ export class ConnectionTreeViewProvider implements vscode.TreeDataProvider<vscod
}

async getChildren(element?: vscode.TreeItem | undefined) {
if (element == null) {
if (!element) {
const settings = load();
const connectionName = this.context.globalState.get("universal.connection");

Expand All @@ -28,7 +29,16 @@ export class ConnectionTreeViewProvider implements vscode.TreeDataProvider<vscod
url: settings.url,
allowInvalidCertificate: false,
windowsAuth: false
}, !connectionName || connectionName === 'Default'))
}, !connectionName || connectionName === 'Default'));
}

var localDevConfig = this.context.globalState.get<LocalDevConfig>("psu.dev.config");

if (localDevConfig) {
items.push(new ConnectionTreeItem({
name: "Local Development",
url: `http://localhost:${localDevConfig.browserPort || 5000}`,
}, !connectionName || connectionName === 'Local Development'));
}

return items;
Expand Down
13 changes: 7 additions & 6 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
import { Universal } from './universal';
import { Container } from './container';
import { DashboardTreeViewProvider } from './dashboard-treeview';
import { InfoTreeViewProvider } from './info-treeview';
import help from './commands/helpCommand';
import { downloadUniversalCommand, downloadUniversal } from './commands/downloadUniversal';
import { load, SetUrl } from './settings';
import { load } from './settings';
import { registerDashboardCommands } from './commands/dashboards';
import { ApiTreeViewProvider } from './api-treeview';
import { registerEndpointCommands } from './commands/endpoints';
Expand All @@ -23,6 +20,8 @@ import { registerTerminalCommands } from './commands/terminals';
import { PlatformTreeViewProvider } from './platform-treeview';
import { registerModuleCommands } from './commands/modules';
import { registerDebuggerCommands } from './commands/debugger';
import { registerLocalDevCommands } from './commands/localDev';
import { LocalDevConfig } from './types';

export async function activate(context: vscode.ExtensionContext) {
registerConnectCommands(context);
Expand Down Expand Up @@ -102,7 +101,7 @@ export async function activate(context: vscode.ExtensionContext) {
vscode.commands.executeCommand('powershell-universal.refreshPlatformTreeView');
});

downloadUniversalCommand();
registerLocalDevCommands(context);
help();
registerDashboardCommands(context);
registerEndpointCommands(context);
Expand All @@ -114,7 +113,9 @@ export async function activate(context: vscode.ExtensionContext) {
registerModuleCommands(context);
registerDebuggerCommands(context);

if (Container.universal.hasConnection()) {
var localDevConfig = context.globalState.get<LocalDevConfig>("psu.dev.config");

if (Container.universal.hasConnection() && !localDevConfig) {
if (await Container.universal.waitForAlive()) {
await Container.universal.connectDebugger();
await Container.universal.installAndLoadModule();
Expand Down
6 changes: 3 additions & 3 deletions src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ export interface ISettings {

export interface IConnection {
name: string;
appToken: string;
appToken?: string | null;
url: string;
allowInvalidCertificate: boolean;
windowsAuth: boolean;
allowInvalidCertificate?: boolean;
windowsAuth?: boolean;
}

export function load(): ISettings {
Expand Down
Loading