@@ -11,7 +11,10 @@ import {
1111 Position ,
1212} from 'vscode' ;
1313import * as path from 'node:path' ;
14- import { execSync } from 'node:child_process' ;
14+ import { exec as execCb } from 'node:child_process' ;
15+ import { promisify } from 'node:util' ;
16+
17+ const exec = promisify ( execCb ) ;
1518
1619const SHOPIFY_CLI_COMMAND = 'shopify theme profile' ;
1720
@@ -41,53 +44,53 @@ export class LiquidProfiler {
4144 borderRadius : '3px' ,
4245 } ) ;
4346
44- private _panel : WebviewPanel | undefined ;
45- private _disposables : Disposable [ ] = [ ] ;
46- private _decorations = new Map < string , DecorationOptions [ ] > ( ) ;
47- private _context : ExtensionContext ;
47+ private panel : WebviewPanel | undefined ;
48+ private disposables : Disposable [ ] = [ ] ;
49+ private decorations = new Map < string , DecorationOptions [ ] > ( ) ;
50+ private context : ExtensionContext ;
4851
4952 constructor ( context : ExtensionContext ) {
50- this . _context = context ;
53+ this . context = context ;
5154 }
5255
5356 public async showProfileForUrl ( url : string ) {
54- const profile = LiquidProfiler . getProfileContents ( url ) ;
57+ const profile = await fetchProfileContents ( url ) ;
5558 await this . processAndShowDecorations ( profile ) ;
5659 await this . showWebviewPanelForProfile ( profile , url ) ;
5760 }
5861
5962 private async showWebviewPanelForProfile ( profile : string , url : string ) {
6063 const column = ViewColumn . Beside ;
6164
62- if ( this . _panel ) {
63- this . _panel . reveal ( column ) ;
64- this . _panel . title = `Liquid Profile: ${ url } ` ;
65- this . _panel . webview . html = '' ;
65+ if ( this . panel ) {
66+ this . panel . reveal ( column ) ;
67+ this . panel . title = `Liquid Profile: ${ url } ` ;
68+ this . panel . webview . html = '' ;
6669 } else {
67- this . _panel = window . createWebviewPanel ( 'liquidProfile' , `Liquid Profile: ${ url } ` , column , {
70+ this . panel = window . createWebviewPanel ( 'liquidProfile' , `Liquid Profile: ${ url } ` , column , {
6871 enableScripts : true ,
6972 // Allow files in the user's workspace (.tmp directory) to be used as local resources
7073 localResourceRoots : [
7174 ...( workspace . workspaceFolders
7275 ? workspace . workspaceFolders . map ( ( folder ) => folder . uri )
7376 : [ ] ) ,
74- Uri . file ( this . _context . asAbsolutePath ( path . join ( 'dist' , 'node' , 'speedscope' ) ) ) ,
77+ Uri . file ( this . context . asAbsolutePath ( path . join ( 'dist' , 'node' , 'speedscope' ) ) ) ,
7578 ] ,
7679 } ) ;
77- this . _panel . onDidDispose ( ( ) => this . dispose ( ) , null , this . _disposables ) ;
80+ this . panel . onDidDispose ( ( ) => this . dispose ( ) , null , this . disposables ) ;
7881 }
7982
80- this . _panel . webview . html = await this . _getSpeedscopeHtml ( profile ) ;
83+ this . panel . webview . html = await this . _getSpeedscopeHtml ( profile ) ;
8184 }
8285
8386 private _getSpeedscopeWebviewUri ( fileName : string ) : Uri {
8487 const filePath = path . join ( 'dist' , 'node' , 'speedscope' , fileName ) ;
85- return this . _panel ! . webview . asWebviewUri ( Uri . file ( this . _context . asAbsolutePath ( filePath ) ) ) ;
88+ return this . panel ! . webview . asWebviewUri ( Uri . file ( this . context . asAbsolutePath ( filePath ) ) ) ;
8689 }
8790
8891 private async _getSpeedscopeHtml ( profileContents : string ) {
8992 const indexHtmlPath = Uri . file (
90- this . _context . asAbsolutePath ( path . join ( 'dist' , 'node' , 'speedscope' , 'index.html' ) ) ,
93+ this . context . asAbsolutePath ( path . join ( 'dist' , 'node' , 'speedscope' , 'index.html' ) ) ,
9194 ) ;
9295 let htmlContent = Buffer . from ( await workspace . fs . readFile ( indexHtmlPath ) ) . toString ( 'utf8' ) ;
9396
@@ -105,7 +108,7 @@ export class LiquidProfiler {
105108 const tmpDir = workspace . workspaceFolders ?. [ 0 ] . uri . fsPath ;
106109 const tmpFile = path . join ( tmpDir ! , '.tmp' , 'profile.json' ) ;
107110 await workspace . fs . writeFile ( Uri . file ( tmpFile ) , Buffer . from ( profileContents ) ) ;
108- const tmpUri = this . _panel ! . webview . asWebviewUri ( Uri . file ( tmpFile ) ) ;
111+ const tmpUri = this . panel ! . webview . asWebviewUri ( Uri . file ( tmpFile ) ) ;
109112 htmlContent = htmlContent . replace (
110113 '<body>' ,
111114 `<body><script>window.location.hash = "profileURL=${ encodeURIComponent (
@@ -117,14 +120,14 @@ export class LiquidProfiler {
117120 }
118121
119122 public dispose ( ) {
120- this . _panel ?. dispose ( ) ;
121- while ( this . _disposables . length ) {
122- const disposable = this . _disposables . pop ( ) ;
123+ this . panel ?. dispose ( ) ;
124+ while ( this . disposables . length ) {
125+ const disposable = this . disposables . pop ( ) ;
123126 if ( disposable ) {
124127 disposable . dispose ( ) ;
125128 }
126129 }
127- this . _panel = undefined ;
130+ this . panel = undefined ;
128131 }
129132
130133 /**
@@ -174,7 +177,7 @@ export class LiquidProfiler {
174177 console . log ( '[Liquid Profiler] Processing profile results for decorations' ) ;
175178
176179 // Clear existing decorations
177- this . _decorations . clear ( ) ;
180+ this . decorations . clear ( ) ;
178181 const visibleEditorsToClear = window . visibleTextEditors ;
179182 for ( const editor of visibleEditorsToClear ) {
180183 editor . setDecorations ( this . fileDecorationType , [ ] ) ;
@@ -215,7 +218,7 @@ export class LiquidProfiler {
215218 } ;
216219
217220 // Store the file-level decoration.
218- this . _decorations . set ( uri . fsPath , [ decoration ] ) ;
221+ this . decorations . set ( uri . fsPath , [ decoration ] ) ;
219222
220223 const visibleEditors = window . visibleTextEditors ;
221224 // Store the paths it's been applied to in a set.
@@ -279,9 +282,9 @@ export class LiquidProfiler {
279282 } ;
280283
281284 // Store the decoration in a map where the key is the file path and the value is an array of decorations
282- const fileDecorations = this . _decorations . get ( uri . fsPath ) || [ ] ;
285+ const fileDecorations = this . decorations . get ( uri . fsPath ) || [ ] ;
283286 fileDecorations . push ( decoration ) ;
284- this . _decorations . set ( uri . fsPath , fileDecorations ) ;
287+ this . decorations . set ( uri . fsPath , fileDecorations ) ;
285288 } catch ( err ) {
286289 console . error (
287290 `[Liquid Profiler] Error creating line decoration for ${ frame . file } :${ frame . line } :` ,
@@ -294,15 +297,15 @@ export class LiquidProfiler {
294297 const visibleEditors = window . visibleTextEditors ;
295298 for ( const editor of visibleEditors ) {
296299 // Get stored decorations for this file
297- const lineDecorations = this . _decorations . get ( editor . document . uri . fsPath ) || [ ] ;
300+ const lineDecorations = this . decorations . get ( editor . document . uri . fsPath ) || [ ] ;
298301 editor . setDecorations ( this . lineDecorationType , lineDecorations ) ;
299302 }
300303
301304 // Add listener for active editor changes
302- this . _context . subscriptions . push (
305+ this . context . subscriptions . push (
303306 window . onDidChangeActiveTextEditor ( ( editor ) => {
304307 if ( editor ) {
305- const decorations = this . _decorations . get ( editor . document . uri . fsPath ) ;
308+ const decorations = this . decorations . get ( editor . document . uri . fsPath ) ;
306309 if ( decorations ) {
307310 editor . setDecorations ( this . lineDecorationType , decorations ) ;
308311 } else {
@@ -328,28 +331,30 @@ export class LiquidProfiler {
328331 // Slow: Red
329332 return '#f44336' ;
330333 }
334+ }
331335
332- private static getProfileContents ( url : string ) {
333- try {
334- console . log ( '[Liquid Profiler] Attempting to load preview for URL:' , url ) ;
335- const result = execSync ( `${ SHOPIFY_CLI_COMMAND } --url=${ url } --json` , { stdio : 'pipe' } ) ;
336- // Remove all characters leading up to the first {
337- const content = result . toString ( ) . replace ( / ^ [ ^ { ] + / , '' ) ;
338- console . log ( `[Liquid Profiler] Successfully retrieved preview content ${ content } ` ) ;
339- return content ;
340- } catch ( error ) {
341- console . error ( '[Liquid Profiler] Error loading preview:' , error ) ;
342- if ( error instanceof Error ) {
343- // If there's stderr output, it will be in error.stderr
344- const errorMessage = ( error as any ) . stderr ?. toString ( ) || error . message ;
345- console . error ( '[Liquid Profiler] Error details:' , errorMessage ) ;
346- return `<div style="color: red; padding: 20px;">
336+ export async function fetchProfileContents ( url : string ) {
337+ try {
338+ console . log ( '[Liquid Profiler] Attempting to load preview for URL:' , url ) ;
339+ const { stdout : result , stderr } = await exec ( `${ SHOPIFY_CLI_COMMAND } --url=${ url } --json` ) ;
340+ if ( stderr ) console . error ( stderr ) ;
341+
342+ // Remove all characters leading up to the first {
343+ const content = result . toString ( ) . replace ( / ^ [ ^ { ] + / , '' ) ;
344+ console . log ( `[Liquid Profiler] Successfully retrieved preview content ${ content } ` ) ;
345+ return content ;
346+ } catch ( error ) {
347+ console . error ( '[Liquid Profiler] Error loading preview:' , error ) ;
348+ if ( error instanceof Error ) {
349+ // If there's stderr output, it will be in error.stderr
350+ const errorMessage = ( error as any ) . stderr ?. toString ( ) || error . message ;
351+ console . error ( '[Liquid Profiler] Error details:' , errorMessage ) ;
352+ return `<div style="color: red; padding: 20px;">
347353 <h3>Error loading preview:</h3>
348354 <pre>${ errorMessage } </pre>
349355 </div>` ;
350- }
351- console . error ( '[Liquid Profiler] Unexpected error type:' , typeof error ) ;
352- return '<div style="color: red; padding: 20px;">An unexpected error occurred</div>' ;
353356 }
357+ console . error ( '[Liquid Profiler] Unexpected error type:' , typeof error ) ;
358+ return '<div style="color: red; padding: 20px;">An unexpected error occurred</div>' ;
354359 }
355360}
0 commit comments