Skip to content

Commit 381bb9e

Browse files
authored
Merge pull request #441 from coderoad/refactor
Refactor
2 parents ff6eb22 + e64b473 commit 381bb9e

18 files changed

+124
-110
lines changed

Diff for: src/actions/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ export { default as onTutorialConfigContinue } from './onTutorialConfigContinue'
44
export { default as onValidateSetup } from './onValidateSetup'
55
export { default as onRunReset } from './onRunReset'
66
export { default as onErrorPage } from './onErrorPage'
7-
export { runTest, onTestPass } from './onTest'
7+
export { runTest } from './onTest'
88
export { onOpenLogs } from './onOpenLogs'

Diff for: src/actions/onStartup.ts

+5-10
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
import * as vscode from 'vscode'
2-
import * as T from 'typings'
32
import * as TT from 'typings/tutorial'
43
import * as E from 'typings/error'
54
import Context from '../services/context/context'
5+
import { send } from '../commands'
66
import { WORKSPACE_ROOT, TUTORIAL_URL } from '../environment'
77
import fetch from 'node-fetch'
8-
import logger from '../services/logger'
98

10-
const onStartup = async (
11-
context: Context,
12-
workspaceState: vscode.Memento,
13-
send: (action: T.Action) => Promise<void>,
14-
): Promise<void> => {
9+
const onStartup = async (context: Context): Promise<void> => {
1510
try {
1611
// check if a workspace is open, otherwise nothing works
1712
const noActiveWorkspace = !WORKSPACE_ROOT.length
@@ -38,9 +33,9 @@ const onStartup = async (
3833
// continue from tutorial from local storage
3934
const tutorial: TT.Tutorial | null = context.tutorial.get()
4035

41-
// no stored tutorial, must start new tutorial
36+
// NEW: no stored tutorial, must start new tutorial
4237
if (!tutorial || !tutorial.id) {
43-
if (TUTORIAL_URL) {
38+
if (!!TUTORIAL_URL) {
4439
// NEW_FROM_URL
4540
try {
4641
const tutorialRes = await fetch(TUTORIAL_URL)
@@ -52,7 +47,7 @@ const onStartup = async (
5247
console.log(`Failed to load tutorial from url ${TUTORIAL_URL} with error "${e.message}"`)
5348
}
5449
}
55-
// NEW
50+
// NEW from start click
5651
send({ type: 'START_NEW_TUTORIAL', payload: { env } })
5752
return
5853
}

Diff for: src/actions/onTest.ts

-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
1-
import * as git from '../services/git'
21
import * as T from 'typings'
32
import * as vscode from 'vscode'
43
import { COMMANDS } from '../commands'
5-
import Context from '../services/context/context'
6-
7-
export const onTestPass = (action: T.Action, context: Context): void => {
8-
context.position.set({ ...action.payload.position, complete: true })
9-
git.saveCommit('Save progress')
10-
}
114

125
export const runTest = (action?: T.Action): void => {
136
vscode.commands.executeCommand(COMMANDS.RUN_TEST, action?.payload)

Diff for: src/actions/onTutorialConfigContinue.ts

+9-7
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,22 @@ import * as T from 'typings'
33
import * as TT from 'typings/tutorial'
44
import Context from '../services/context/context'
55
import tutorialConfig from './utils/tutorialConfig'
6-
import { COMMANDS } from '../commands'
6+
import { COMMANDS, send } from '../commands'
7+
import logger from '../services/logger'
78

8-
const onTutorialConfigContinue = async (action: T.Action, context: Context, send: T.Send): Promise<void> => {
9+
const onTutorialConfigContinue = async (action: T.Action, context: Context): Promise<void> => {
10+
logger('onTutorialConfigContinue', action)
911
try {
10-
const tutorialContinue: TT.Tutorial | null = context.tutorial.get()
11-
if (!tutorialContinue) {
12+
const tutorialToContinue: TT.Tutorial | null = context.tutorial.get()
13+
if (!tutorialToContinue) {
1214
throw new Error('Invalid tutorial to continue')
1315
}
16+
// update the current stepId on startup
17+
vscode.commands.executeCommand(COMMANDS.SET_CURRENT_POSITION, action.payload.position)
1418
await tutorialConfig({
15-
data: tutorialContinue,
19+
data: tutorialToContinue,
1620
alreadyConfigured: true,
1721
})
18-
// update the current stepId on startup
19-
vscode.commands.executeCommand(COMMANDS.SET_CURRENT_POSITION, action.payload.position)
2022
} catch (e) {
2123
const error = {
2224
type: 'UnknownError',

Diff for: src/actions/onTutorialConfigNew.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import { onEvent } from '../services/telemetry'
77
import { version, compareVersions } from '../services/dependencies'
88
import Context from '../services/context/context'
99
import tutorialConfig from './utils/tutorialConfig'
10+
import { send } from '../commands'
1011

11-
const onTutorialConfigNew = async (action: T.Action, context: Context, send: T.Send): Promise<void> => {
12+
const onTutorialConfigNew = async (action: T.Action, context: Context): Promise<void> => {
1213
try {
1314
const data: TT.Tutorial = action.payload.tutorial
1415

Diff for: src/actions/onValidateSetup.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import * as T from 'typings'
22
import * as E from 'typings/error'
33
import { version } from '../services/dependencies'
44
import { checkWorkspaceEmpty } from '../services/workspace'
5+
import { send } from '../commands'
56

6-
const onValidateSetup = async (send: T.Send): Promise<void> => {
7+
const onValidateSetup = async (): Promise<void> => {
78
try {
89
// check workspace is selected
910
const isEmptyWorkspace = await checkWorkspaceEmpty()

Diff for: src/actions/utils/tutorialConfig.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const tutorialConfig = async ({ data, alreadyConfigured }: TutorialConfigParams)
5252
}
5353
}
5454

55-
await vscode.commands.executeCommand(COMMANDS.CONFIG_TEST_RUNNER, data)
55+
await vscode.commands.executeCommand(COMMANDS.CONFIG_TEST_RUNNER, { data, alreadyConfigured })
5656

5757
if (!DISABLE_RUN_ON_SAVE) {
5858
// verify if file test should run based on document saved

Diff for: src/channel.ts

+7-38
Original file line numberDiff line numberDiff line change
@@ -9,47 +9,36 @@ import * as hooks from './services/hooks'
99

1010
interface Channel {
1111
receive(action: T.Action): Promise<void>
12-
send(action: T.Action): Promise<void>
13-
}
14-
15-
interface ChannelProps {
16-
postMessage: (action: T.Action) => Thenable<boolean>
17-
workspaceState: vscode.Memento
1812
}
1913

2014
class Channel implements Channel {
21-
private postMessage: (action: T.Action) => Thenable<boolean>
22-
private workspaceState: vscode.Memento
23-
private context: Context
24-
constructor({ postMessage, workspaceState }: ChannelProps) {
25-
// workspaceState used for local storage
26-
this.workspaceState = workspaceState
27-
this.postMessage = postMessage
15+
public context: Context
16+
constructor(workspaceState: vscode.Memento) {
17+
// workspaceState used for local storages
2818
this.context = new Context(workspaceState)
2919
}
3020

3121
// receive from webview
3222
public receive = async (action: T.Action): Promise<void> => {
3323
// action may be an object.type or plain string
3424
const actionType: string = typeof action === 'string' ? action : action.type
35-
// const onError = (error: T.ErrorMessage) => this.send({ type: 'ERROR', payload: { error } })
3625

3726
logger(`EXT RECEIVED: "${actionType}"`)
3827

3928
switch (actionType) {
4029
case 'EDITOR_STARTUP':
41-
actions.onStartup(this.context, this.workspaceState, this.send)
30+
actions.onStartup(this.context)
4231
return
4332
// clear tutorial local storage
4433
// configure test runner, language, git
4534
case 'EDITOR_TUTORIAL_CONFIG':
46-
actions.onTutorialConfigNew(action, this.context, this.send)
35+
actions.onTutorialConfigNew(action, this.context)
4736
return
4837
case 'EDITOR_TUTORIAL_CONTINUE_CONFIG':
49-
actions.onTutorialConfigContinue(action, this.context, this.send)
38+
actions.onTutorialConfigContinue(action, this.context)
5039
return
5140
case 'EDITOR_VALIDATE_SETUP':
52-
actions.onValidateSetup(this.send)
41+
actions.onValidateSetup()
5342
return
5443
case 'EDITOR_REQUEST_WORKSPACE':
5544
openWorkspace()
@@ -95,26 +84,6 @@ class Channel implements Channel {
9584
return
9685
}
9786
}
98-
// send to webview
99-
public send = async (action: T.Action): Promise<void> => {
100-
// load error page if error action is triggered
101-
actions.onErrorPage(action)
102-
// action may be an object.type or plain string
103-
const actionType: string = typeof action === 'string' ? action : action.type
104-
105-
logger(`EXT TO CLIENT: "${actionType}"`)
106-
107-
switch (actionType) {
108-
case 'TEST_PASS':
109-
actions.onTestPass(action, this.context)
110-
}
111-
112-
// send message
113-
const sentToClient = await this.postMessage(action)
114-
if (!sentToClient) {
115-
throw new Error(`Message post failure: ${JSON.stringify(action)}`)
116-
}
117-
}
11887
}
11988

12089
export default Channel

Diff for: src/commands.ts

+25-7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import createTestRunner from './services/testRunner'
55
import createWebView from './services/webview'
66
import * as hooks from './services/hooks'
77
import logger from './services/logger'
8+
import * as actions from './actions'
9+
import Channel from './channel'
810

911
export const COMMANDS = {
1012
START: 'coderoad.start',
@@ -26,14 +28,20 @@ let sendToClient = (action: T.Action): void => {
2628
// This makes it easier to pass the send
2729
// function throughout the codebase
2830
export const send = (action: T.Action): void => {
29-
sendToClient(action)
31+
// load error page if error action is triggered
32+
actions.onErrorPage(action)
33+
34+
logger(`EXT TO CLIENT: "${typeof action === 'string' ? action : action.type}"`)
35+
36+
if (action) sendToClient(action)
3037
}
3138

3239
export const createCommands = ({ extensionPath, workspaceState }: CreateCommandProps): { [key: string]: any } => {
3340
// React panel webview
3441
let webview: any
3542
let currentPosition: T.Position
3643
let testRunner: any
44+
const channel = new Channel(workspaceState)
3745

3846
return {
3947
// initialize
@@ -42,24 +50,33 @@ export const createCommands = ({ extensionPath, workspaceState }: CreateCommandP
4250
webview.createOrShow()
4351
} else {
4452
// activate machine
45-
webview = createWebView({
53+
webview = await createWebView({
4654
extensionPath,
47-
workspaceState,
55+
channel,
4856
})
4957
// make send to client function exportable
5058
// as "send".
5159
sendToClient = webview.send
5260
}
5361
},
54-
[COMMANDS.CONFIG_TEST_RUNNER]: async (data: TT.Tutorial) => {
55-
const setupActions = data.config.setup
56-
if (setupActions) {
57-
hooks.onInit(setupActions)
62+
[COMMANDS.CONFIG_TEST_RUNNER]: async ({
63+
data,
64+
alreadyConfigured,
65+
}: {
66+
data: TT.Tutorial
67+
alreadyConfigured: boolean
68+
}) => {
69+
if (!alreadyConfigured) {
70+
const setupActions = data.config.setup
71+
if (setupActions) {
72+
hooks.onInit(setupActions)
73+
}
5874
}
5975
testRunner = createTestRunner(data, {
6076
onSuccess: (position: T.Position) => {
6177
logger('test pass position', position)
6278
// send test pass message back to client
79+
channel.context.position.set({ ...position, complete: true })
6380
send({ type: 'TEST_PASS', payload: { position: { ...position, complete: true } } })
6481
},
6582
onFail: (position: T.Position, failSummary: T.TestFail): void => {
@@ -83,6 +100,7 @@ export const createCommands = ({ extensionPath, workspaceState }: CreateCommandP
83100
[COMMANDS.SET_CURRENT_POSITION]: (position: T.Position) => {
84101
// set from last setup stepAction
85102
currentPosition = position
103+
channel.context.position.set(position)
86104
},
87105
[COMMANDS.RUN_TEST]: ({
88106
subtasks,

Diff for: src/extension.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import * as vscode from 'vscode'
22
import { createCommands } from './commands'
33
import * as telemetry from './services/telemetry'
44

5-
let onDeactivate = () => {}
5+
let onDeactivate = () => {
6+
/* placeholder for unsubscribing fn */
7+
}
68

79
// activate run on vscode extension initialization
810
export const activate = (vscodeExt: vscode.ExtensionContext): void => {

Diff for: src/services/git/index.ts

+31
Original file line numberDiff line numberDiff line change
@@ -160,3 +160,34 @@ export async function loadCommitHistory(): Promise<string[]> {
160160
export function getShortHash(hash: string): string {
161161
return hash.slice(0, 7)
162162
}
163+
164+
export async function getCommitMessage(hash: string): Promise<string | null> {
165+
try {
166+
// returns an list of commit hashes
167+
const { stdout, stderr } = await exec({ command: `git log -n 1 --pretty=format:%s ${hash}` })
168+
if (stderr) {
169+
return null
170+
}
171+
// string match on remote output
172+
return stdout
173+
} catch (error) {
174+
logger('error', error)
175+
// likely no git commit message found
176+
return null
177+
}
178+
}
179+
180+
export async function commitsExistsByMessage(message: string): Promise<boolean> {
181+
try {
182+
// returns a list of commit hashes
183+
const { stdout, stderr } = await exec({ command: `git log -g --grep='${message}'` })
184+
if (stderr) {
185+
return false
186+
}
187+
return !!stdout.length
188+
} catch (error) {
189+
logger('error', error)
190+
// likely no commit found
191+
return false
192+
}
193+
}

Diff for: src/services/hooks/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as TT from 'typings/tutorial'
22
import * as git from '../git'
3-
import loadCommits from './utils/loadCommits'
3+
import { loadCommits } from './utils/commits'
44
import { loadWatchers, resetWatchers } from './utils/watchers'
55
import openFiles from './utils/openFiles'
66
import runCommands from './utils/runCommands'
@@ -48,6 +48,7 @@ export const onError = async (error: Error): Promise<void> => {
4848
}
4949

5050
export const onStepComplete = async ({ levelId, stepId }: { levelId: string; stepId: string }): Promise<void> => {
51+
git.saveCommit(`Save progress: ${stepId}`)
5152
logger(`ON STEP COMPLETE: ${JSON.stringify({ levelId, stepId })}`)
5253
}
5354

Diff for: src/services/hooks/utils/commits.ts

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import * as git from '../../git'
2+
3+
// avoid duplicate commits
4+
const verifyCommitUnique = async (hash: string): Promise<boolean> => {
5+
const message: string | null = await git.getCommitMessage(hash)
6+
if (!message) {
7+
return false
8+
}
9+
const exists: boolean = await git.commitsExistsByMessage(message)
10+
return exists
11+
}
12+
13+
export const loadCommits = async (commits: string[] = []): Promise<void> => {
14+
if (commits && commits.length) {
15+
// load the current list of commits for validation
16+
for (const commit of commits) {
17+
const commitExists = await verifyCommitUnique(commit)
18+
if (!commitExists) {
19+
await git.loadCommit(commit)
20+
}
21+
}
22+
}
23+
}

Diff for: src/services/hooks/utils/loadCommits.ts

-12
This file was deleted.

Diff for: src/services/logger/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { LOG } from '../../environment'
22

3-
export type Log = string | number | object | null | undefined // eslint-disable-line
3+
export type Log = any
44

55
const logger = (...messages: Log[]): void => {
66
if (!LOG) {

0 commit comments

Comments
 (0)