11import * as T from 'typings'
22import * as TT from 'typings/tutorial'
3+ import * as E from 'typings/error'
34import * as vscode from 'vscode'
45import saveCommit from '../actions/saveCommit'
56import setupActions from '../actions/setupActions'
@@ -10,6 +11,11 @@ import logger from '../services/logger'
1011import Context from './context'
1112import { version as gitVersion } from '../services/git'
1213import { openWorkspace , checkWorkspaceEmpty } from '../services/workspace'
14+ import { readFile } from 'fs'
15+ import { join } from 'path'
16+ import { promisify } from 'util'
17+
18+ const readFileAsync = promisify ( readFile )
1319
1420interface Channel {
1521receive ( action :T . Action ) :Promise < void >
@@ -39,7 +45,9 @@ class Channel implements Channel {
3945public receive = async ( action :T . Action ) => {
4046// action may be an object.type or plain string
4147const actionType :string = typeof action === 'string' ?action :action . type
42- const onError = ( error :T . ErrorMessage ) => this . send ( { type :'ERROR' , payload :{ error} } )
48+ // const onError = (error: T.ErrorMessage) => this.send({ type: 'ERROR', payload: { error } })
49+
50+ // console.log(`ACTION: ${actionType}`)
4351
4452switch ( actionType ) {
4553case 'EDITOR_ENV_GET' :
@@ -86,7 +94,16 @@ class Channel implements Channel {
8694// setup tutorial config (save watcher, test runner, etc)
8795await this . context . setTutorial ( this . workspaceState , data )
8896
89- await tutorialConfig ( { config :data . config } , onError )
97+ const error :E . ErrorMessage | void = await tutorialConfig ( { config :data . config } ) . catch ( ( error :Error ) => ( {
98+ type :'UnknownError' ,
99+ message :`Location: tutorial config.\n\n${ error . message } ` ,
100+ } ) )
101+
102+ // has error
103+ if ( error && error . type ) {
104+ this . send ( { type :'TUTORIAL_CONFIGURE_FAIL' , payload :{ error} } )
105+ return
106+ }
90107
91108// report back to the webview that setup is complete
92109this . send ( { type :'TUTORIAL_CONFIGURED' } )
@@ -97,34 +114,54 @@ class Channel implements Channel {
97114throw new Error ( 'Invalid tutorial to continue' )
98115}
99116const continueConfig :TT . TutorialConfig = tutorialContinue . config
100- await tutorialConfig (
101- {
102- config :continueConfig ,
103- alreadyConfigured :true ,
104- } ,
105- onError ,
106- )
117+ await tutorialConfig ( {
118+ config :continueConfig ,
119+ alreadyConfigured :true ,
120+ } )
107121// update the current stepId on startup
108122vscode . commands . executeCommand ( COMMANDS . SET_CURRENT_STEP , action . payload )
109123return
110124case 'EDITOR_VALIDATE_SETUP' :
111125// 1. check workspace is selected
112126const isEmptyWorkspace = await checkWorkspaceEmpty ( this . workspaceRoot . uri . path )
113127if ( ! isEmptyWorkspace ) {
114- this . send ( { type :'NOT_EMPTY_WORKSPACE' } )
128+ const error :E . ErrorMessage = {
129+ type :'WorkspaceNotEmpty' ,
130+ message :'' ,
131+ actions :[
132+ {
133+ label :'Open Workspace' ,
134+ transition :'REQUEST_WORKSPACE' ,
135+ } ,
136+ {
137+ label :'Check Again' ,
138+ transition :'RETRY' ,
139+ } ,
140+ ] ,
141+ }
142+ this . send ( { type :'VALIDATE_SETUP_FAILED' , payload :{ error} } )
115143return
116144}
117145// 2. check Git is installed.
118146// Should wait for workspace before running otherwise requires access to root folder
119147const isGitInstalled = await gitVersion ( )
120148if ( ! isGitInstalled ) {
121- this . send ( { type :'GIT_NOT_INSTALLED' } )
149+ const error :E . ErrorMessage = {
150+ type :'GitNotFound' ,
151+ message :'' ,
152+ actions :[
153+ {
154+ label :'Check Again' ,
155+ transition :'RETRY' ,
156+ } ,
157+ ] ,
158+ }
159+ this . send ( { type :'VALIDATE_SETUP_FAILED' , payload :{ error} } )
122160return
123161}
124162this . send ( { type :'SETUP_VALIDATED' } )
125163return
126164case 'EDITOR_REQUEST_WORKSPACE' :
127- console . log ( 'request workspace' )
128165openWorkspace ( )
129166return
130167// load step actions (git commits, commands, open files)
@@ -146,6 +183,24 @@ class Channel implements Channel {
146183}
147184// send to webview
148185public send = async ( action :T . Action ) => {
186+ // Error middleware
187+ if ( action ?. payload ?. error ?. type ) {
188+ // load error markdown message
189+ const error = action . payload . error
190+ const errorMarkdownFile = join ( __dirname , '..' , '..' , 'errors' , `${ action . payload . error . type } .md` )
191+ const errorMarkdown = await readFileAsync ( errorMarkdownFile ) . catch ( ( ) => {
192+ // onError(new Error(`Error Markdown file not found for ${action.type}`))
193+ } )
194+
195+ // log error to console for safe keeping
196+ console . log ( `ERROR:\n${ errorMarkdown } ` )
197+
198+ if ( errorMarkdown ) {
199+ // add a clearer error message for the user
200+ error . message = `${ errorMarkdown } \n${ error . message } `
201+ }
202+ }
203+
149204// action may be an object.type or plain string
150205const actionType :string = typeof action === 'string' ?action :action . type
151206switch ( actionType ) {
@@ -160,8 +215,9 @@ class Channel implements Channel {
160215saveCommit ( )
161216}
162217
163- const success = await this . postMessage ( action )
164- if ( ! success ) {
218+ // send message
219+ const sentToClient = await this . postMessage ( action )
220+ if ( ! sentToClient ) {
165221throw new Error ( `Message post failure:${ JSON . stringify ( action ) } ` )
166222}
167223}