UNPKG

sculptui-vscode

Version:

Visually edit your React component code from the browser

205 lines (191 loc) 7.37 kB
import fs, { stat } from 'fs'; import path from 'path'; import { commands, ExtensionContext, ExtensionMode, Uri } from 'vscode'; import StatusBar from './status-bar/StatusBar'; import DevServer, { DevServerStatus } from './server/DevServer'; import { getProjectFolder, getWorkspaceFolder, traverseSubFolders, isWorkspacesRoot } from './utils/folders'; import { detectScriptsVersion } from '@sculpt-ui/server'; import SculptServer, { SculptServerStatus } from '@sculpt-ui/source-server/lib/SculptServer'; import telemetry from './telemetry'; import { Request, RequestHandler, Response } from 'express'; import { SourceRef } from '@sculpt-ui/core/lib/types'; import { selectCode } from './utils/vscode-file'; import { log, logError } from './utils/vscode-output'; process.env.REACT_EDITOR = 'code'; let statusBar: StatusBar; let sculptServer: SculptServer; let devServer: DevServer | undefined = undefined; let gStoragePath = ''; const urlPathPrefix = '/_sculpt'; const localDevMode: string = 'dev-local'; const clientHandler: (x: string) => RequestHandler = (assetsFolder: string) => (req: Request, res: Response) => { let reqPathBase = urlPathPrefix; if (req.params.key && req.params.key !== 'static') { reqPathBase += `/${req.params.key}`; } const subPath = req.originalUrl.substr(reqPathBase.length + 1); const filePath = path.resolve(assetsFolder, 'sculpt-client', subPath); fs.access(filePath, fs.constants.F_OK, err => { if (err) { if (req.accepts(['html', 'text/html'])) { res.sendFile('sculpt-client/index.html', { root: assetsFolder }); } else { res.status(404).end(); } } else { res.sendFile(`sculpt-client/${subPath}`, { root: assetsFolder }); } }); }; const extensionsHandler: (x: string, packagesFolder: string) => RequestHandler = ( assetsFolder: string, packagesFolder: string, ) => (req: Request, res: Response) => { const subPath = req.originalUrl.substr(req.originalUrl.indexOf('/extensions')); if (process.env.mode === localDevMode && subPath.startsWith('extension/styles/dist')) { res.sendFile(subPath.replace('extension/dist', 'extension'), { root: packagesFolder, }); } else { res.sendFile(subPath, { root: assetsFolder }); } }; const ideService = { getServerInfo: async () => ({ rootPath: process.cwd(), message: 'Sculpt is alive!', ide: 'VS Code', }), selectInEditor: async (id: string, sourceRef: SourceRef) => { selectCode( sourceRef.filePath, { line: sourceRef.endLine ? sourceRef.endLine - 1 : sourceRef.line, character: sourceRef.endColumn ? sourceRef.endColumn - 1 : 0, }, { line: sourceRef.line - 1, character: sourceRef.column ? sourceRef.column - 1 : 0 }, ); }, }; export async function activate(context: ExtensionContext) { const { subscriptions, extensionPath, globalStoragePath } = context; gStoragePath = globalStoragePath; telemetry.activate(context); telemetry.addCommonProperties({ extensionMode: ExtensionMode[context.extensionMode] }); telemetry.sendEvent('activate'); if (!statusBar) { statusBar = new StatusBar(); statusBar.subscribe(subscriptions); } const assetsFolder = Uri.file(path.join(extensionPath, 'assets')).fsPath; const packagesFolder = Uri.file(path.join(extensionPath, '../packages')).fsPath; // IDEServer always activated const workspaceFolder = getWorkspaceFolder(); log(process.cwd()); if (!sculptServer) { sculptServer = new SculptServer({ clientHandler: clientHandler(assetsFolder), ideService: ideService, onLog: message => log(message), showErrorMessage: message => logError(message), extensionsHandler: extensionsHandler(assetsFolder, packagesFolder), onStatusChanged: (status: SculptServerStatus) => statusBar.setConnected(status === SculptServerStatus.connected), }); } const sculptServerUrl = await sculptServer.start(); if (globalStoragePath && workspaceFolder) { if (!fs.existsSync(globalStoragePath)) { fs.mkdirSync(globalStoragePath, { recursive: true }); } const settingsFile = path.join(globalStoragePath, 'sculpt-ide.json'); /* TODO: Use a lock file here */ let ideConfig: { [folder: string]: string } = {}; if (fs.existsSync(settingsFile)) { try { ideConfig = JSON.parse(fs.readFileSync(settingsFile, 'utf8')); } catch (err) { console.error( 'Could not correctly read settings file. Presumably corrupt and will be overwritten.', settingsFile, ); } } ideConfig[workspaceFolder] = sculptServerUrl; fs.writeFileSync(settingsFile, JSON.stringify(ideConfig, null, 2), 'utf8'); } const folder = getProjectFolder(undefined, workspaceFolder); if (!folder) { return; } const rootDetectedScript = detectScriptsVersion(folder); let scriptsAvailable = isWorkspacesRoot(folder) ? rootDetectedScript === 'sculpt' : !!rootDetectedScript; if (!scriptsAvailable) { traverseSubFolders( folder, (dir: string) => { const detected = !!detectScriptsVersion(dir); if (detected) { scriptsAvailable = true; } return detected; }, 3, ['node_modules', '.vscode', '.git'], ); } if (scriptsAvailable) { statusBar.setRunAvailable(true); if (!devServer) { devServer = new DevServer( extensionPath, sculptServerUrl, status => { statusBar.setRunning(status === DevServerStatus.started || status === DevServerStatus.stopping); statusBar.setInTransition(status === DevServerStatus.starting || status === DevServerStatus.stopping); }, (message: string, duration?: number) => { statusBar.setTemporaryMessage(message, duration); }, ); } const start = async () => { const result = await devServer?.start(); if (result) sculptServer.tryToInitProject(result); }; subscriptions.push( commands.registerCommand('sculpt-ui.start', start), commands.registerCommand('sculpt-ui.stop', devServer.stop), ); } } // this method is called when your extension is deactivated export function deactivate() { const workspaceFolder = getWorkspaceFolder(); if (gStoragePath && workspaceFolder) { if (!fs.existsSync(gStoragePath)) { fs.mkdirSync(gStoragePath, { recursive: true }); } const settingsFile = path.join(gStoragePath, 'sculpt-ide.json'); let ideConfig: { [folder: string]: string } = {}; if (fs.existsSync(settingsFile)) { try { ideConfig = JSON.parse(fs.readFileSync(settingsFile, 'utf8')); } catch (err) { console.error( 'Could not correctly read settings file. Presumably corrupt and will be overwritten.', settingsFile, ); } } delete ideConfig[workspaceFolder]; fs.writeFileSync(settingsFile, JSON.stringify(ideConfig, null, 2), 'utf8'); if (devServer) { devServer.stop(); } if (sculptServer) { sculptServer.stop(); } telemetry.sendEvent('deactivate'); telemetry.deactivate(); } }