import * as path from 'path' ; import * as vscode from 'vscode' ; import * as fs from 'fs' ; import * as md5 from "md5" ; import getFileDocEntries , { DocEntry } from './getFileDocEntries' ; import config from './config' ; const entryFileName = path . join ( __dirname , "./entryFile.json" ) ; let title = "TypeScript UML" ; let docEntries ; let saveData = getSaveFileData ( ) ; let globalPanel ; let globalExtensionPath ; let prevMd5 = null ; export function watchFile ( ) { if ( fs . existsSync ( saveData . entryFile ) ) { if ( ! prevMd5 ) { prevMd5 = md5 ( fs . readFileSync ( saveData . entryFile ) ) ; } fs . watchFile ( saveData . entryFile , ( eventType , filename ) => { if ( filename ) { const currMd5 = md5 ( fs . readFileSync ( saveData . entryFile ) ) ; if ( currMd5 !== prevMd5 ) { UMLWebviewPanel . revive ( globalPanel , globalExtensionPath ) ; prevMd5 = currMd5 ; } } } ) ; } } export function activate ( context ) { let newFile = "" ; async function commandCallback ( fileUri ) { if ( fileUri !== void 0 ) { if ( typeof fileUri === "string" ) { newFile = fileUri ; } else if ( typeof fileUri === "object" && fileUri . path ) { newFile = fileUri . path ; } } else { newFile = "" ; const { activeTextEditor } = vscode . window ; if ( activeTextEditor ) { if ( ! newFile && activeTextEditor && activeTextEditor . document ) { newFile = activeTextEditor . document . fileName ; } } } newFile = path . normalize ( newFile ) ; if ( process . platform === "win32" && newFile . startsWith ( "\\" ) ) { newFile = newFile . slice ( 1 ) ; } if ( newFile !== saveData . entryFile ) { saveData . entryFile = newFile ; saveData . layout = "" ; } fs . unwatchFile ( saveData . entryFile ) ; watchFile ( ) ; UMLWebviewPanel . createOrShow ( context . extensionPath ) ; } context . subscriptions . push ( vscode . commands . registerCommand ( 'tsUML.showFile' , commandCallback ) ) ; context . subscriptions . push ( vscode . commands . registerCommand ( 'tsUML.showFolder' , commandCallback ) ) ; if ( vscode . window . registerWebviewPanelSerializer ) { vscode . window . registerWebviewPanelSerializer ( UMLWebviewPanel . viewType , { async deserializeWebviewPanel ( webviewPanel , state ) { globalPanel = webviewPanel ; globalExtensionPath = context . extensionPath ; UMLWebviewPanel . revive ( webviewPanel , context . extensionPath ) ; } } ) ; } } class UMLWebviewPanel { /**
     * Track the currently panel. Only allow a single panel to exist at a time.
     */ public static currentPanel : UMLWebviewPanel | undefined ; public static readonly viewType = "tsUML" ; private readonly _panel : vscode . WebviewPanel ; private readonly _extensionPath : string ; private _disposables : vscode . Disposable [ ] = [ ] ; public static createOrShow ( extensionPath ) { const column = vscode . window . activeTextEditor ? vscode . window . activeTextEditor . viewColumn : undefined ; if ( UMLWebviewPanel . currentPanel ) { UMLWebviewPanel . currentPanel . _panel . reveal ( column ) ; UMLWebviewPanel . currentPanel . _update ( ) ; return ; } const panel = vscode . window . createWebviewPanel ( UMLWebviewPanel . viewType , title , column || vscode . ViewColumn . One , { enableScripts : true , localResourceRoots : [ vscode . Uri . file ( path . join ( extensionPath , 'media' ) ) , vscode . Uri . file ( path . join ( extensionPath , 'build' ) ) , vscode . Uri . file ( path . join ( extensionPath , 'icons' ) ) ] } ) ; addDidReceiveMessage ( panel ) ; globalPanel = panel ; globalExtensionPath = extensionPath ; UMLWebviewPanel . currentPanel = new UMLWebviewPanel ( panel , extensionPath ) ; } public static revive ( panel , extensionPath ) { UMLWebviewPanel . currentPanel = new UMLWebviewPanel ( panel , extensionPath ) ; } private constructor ( panel , extensionPath ) { this . _panel = panel ; this . _extensionPath = extensionPath ; this . _update ( ) ; this . _panel . onDidDispose ( ( ) => this . dispose ( ) , null , this . _disposables ) ; this . _panel . onDidChangeViewState ( e => { if ( this . _panel . visible ) { } } , null , this . _disposables ) ; } public dispose ( ) { UMLWebviewPanel . currentPanel = undefined ; this . _panel . dispose ( ) ; while ( this . _disposables . length ) { const x = this . _disposables . pop ( ) ; if ( x ) { x . dispose ( ) ; } } } public _update ( ) { this . _panel . webview . html = this . _getHtmlForWebview ( ) ; } private _getHtmlForWebview ( ) { const nonce = getNonce ( ) ; const favicon = vscode . Uri . file ( path . join ( this . _extensionPath , 'icons/ts.png' ) ) ; const faviconUri = favicon . with ( { scheme : 'vscode-resource' } ) ; const appScript = vscode . Uri . file ( path . join ( this . _extensionPath , 'build/js/app.js' ) ) ; const appUri = appScript . with ( { scheme : 'vscode-resource' } ) ; const vendorScript = vscode . Uri . file ( path . join ( this . _extensionPath , 'build/js/vendor.js' ) ) ; const vendorUri = vendorScript . with ( { scheme : 'vscode-resource' } ) ; if ( saveData . entryFile ) { docEntries = getFileDocEntries ( saveData . entryFile , 0 , config ) ; } let layoutStr = JSON . stringify ( saveData . layout ) ; saveDataToFile ( ) ; addDidReceiveMessage ( this . _panel ) ; const htmlStr = `<!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <meta name="viewport" content="width=device-width, initial-scale=1.0">
                <title>${ title }</title>
                <link rel="icon" type="image/png" sizes="32x32" href="${ faviconUri }">
            </head>
            <body>
                <span id="doc-layout" style="display: none;">${ layoutStr }</span>
                <div id="app"></div>
                <script nonce="${ nonce }" type="text/javascript" src="${ appUri }" charset="utf-8"></script>
                <script nonce="${ nonce }" type="text/javascript" src="${ vendorUri }" charset="utf-8"></script>
            </body>
            </html>` ; return htmlStr ; } } function addDidReceiveMessage ( panel ) { let saveDataTimer = null ; panel . webview . onDidReceiveMessage ( message => { if ( message . boardWillMount ) { panel . webview . postMessage ( { docEntries } ) ; } if ( message . getLayout ) { panel . webview . postMessage ( { layout : saveData . layout } ) ; } if ( message . error ) { console . error ( message . error ) ; } if ( message . setLayout ) { saveData . layout = message . setLayout ; clearTimeout ( saveDataTimer ) ; saveDataTimer = setTimeout ( saveDataToFile , 200 ) ; } } ) ; } function getNonce ( ) { let text = "" ; const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ; for ( let i = 0 ; i < 32 ; i ++ ) { text += possible . charAt ( Math . floor ( Math . random ( ) * possible . length ) ) ; } return text ; } function saveDataToFile ( ) { fs . writeFileSync ( entryFileName , JSON . stringify ( saveData , null , 2 ) , { encoding : "utf8" } ) ; } function getSaveFileData ( ) { let data = { entryFile : "" , layout : "" } ; if ( fs . existsSync ( entryFileName ) ) { try { data = JSON . parse ( fs . readFileSync ( entryFileName , { encoding : "utf8" } ) ) ; } catch ( e ) { } } return data ; }