diff --git a/package.json b/package.json index 4239b431..41f1340b 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "compile": "tsc -p ./", "watch": "tsc -watch -p ./", "postinstall": "node ./node_modules/vscode/bin/install", + "storybook": "cd web-app && npm run storybook", "test": "npm run build && node ./node_modules/vscode/bin/test" }, "devDependencies": { diff --git a/src/editor/commands/index.ts b/src/editor/commands/index.ts index 9ea0b92e..f7517e04 100644 --- a/src/editor/commands/index.ts +++ b/src/editor/commands/index.ts @@ -2,11 +2,12 @@ import * as vscode from 'vscode' import { join } from 'path' import { setStorage } from '../storage' import ReactWebView from '../ReactWebView' +import { isEmptyWorkspace } from '../workspace' import * as CR from 'typings' const COMMANDS = { START: 'coderoad.start', - NEW_OR_CONTINUE: 'coderoad.new_or_continue', + TUTORIAL_LAUNCH: 'coderoad.tutorial_launch', OPEN_WEBVIEW: 'coderoad.open_webview', SEND_STATE: 'coderoad.send_state', SEND_DATA: 'coderoad.send_data', @@ -20,12 +21,13 @@ interface CreateCommandProps { machine: CR.StateMachine, storage: any, git: any + position: any } // React panel webview let webview: any; -export const createCommands = ({ context, machine, storage, git }: CreateCommandProps) => ({ +export const createCommands = ({ context, machine, storage, git, position }: CreateCommandProps) => ({ // initialize [COMMANDS.START]: () => { // set local storage workspace @@ -36,25 +38,30 @@ export const createCommands = ({ context, machine, storage, git }: CreateCommand console.log('webview', webview.panel.webview.postMessage) machine.activate() }, - [COMMANDS.NEW_OR_CONTINUE]: async () => { - // verify that the user has a tutorial & progress - // verify git is setup with a coderoad remote - const [tutorial, progress, hasGit, hasGitRemote] = await Promise.all([ - storage.getTutorial(), - storage.getProgress(), - git.gitVersion(), - git.gitCheckRemoteExists(), - ]) - const canContinue = !!(tutorial && progress && hasGit && hasGitRemote) - console.log('canContinue', canContinue) - // if a tutorial exists, 'CONTINUE' - // otherwise start from 'NEW' - machine.send(canContinue ? 'CONTINUE' : 'NEW') - }, // open React webview [COMMANDS.OPEN_WEBVIEW]: (column: number = vscode.ViewColumn.One) => { webview.createOrShow(column); }, + // launch a new tutorial + // NOTE: may be better to move into action as logic is primarily non-vscode + [COMMANDS.TUTORIAL_LAUNCH]: async (tutorial: CR.Tutorial) => { + console.log('launch tutorial') + + await isEmptyWorkspace() + + await git.gitInitIfNotExists() + + // TODO: use actual tutorial repo + await Promise.all([git.gitSetupRemote(tutorial.meta.repo), storage.setTutorial(tutorial), storage.resetProgress()]) + + // TODO: refactor to allow client to call initialization + const pos: CR.Position = await position.getInitial(tutorial) + + // eslint-disable-next-line + const { steps } = tutorial.data + const { setup } = steps[pos.stepId].actions + await git.gitLoadCommits(setup) + }, // open a file [COMMANDS.OPEN_FILE]: async (relativeFilePath: string) => { console.log(`OPEN_FILE ${JSON.stringify(relativeFilePath)}`) @@ -79,6 +86,6 @@ export const createCommands = ({ context, machine, storage, git }: CreateCommand }, [COMMANDS.RECEIVE_ACTION]: (action: string | CR.Action) => { console.log('onReceiveAction', action) - machine.onReceive(action) + machine.send(action) } }) \ No newline at end of file diff --git a/src/editor/commands/start-old.ts b/src/editor/commands/start-old.ts deleted file mode 100644 index 5d172fbc..00000000 --- a/src/editor/commands/start-old.ts +++ /dev/null @@ -1,108 +0,0 @@ -import * as vscode from 'vscode' -import * as CR from 'typings' - -import tutorialSetup from '../../services/tutorialSetup' -import { isEmptyWorkspace, openReadme } from '../workspace' -import { setWorkspaceRoot } from '../../services/node' -import { setStorage } from '../../editor/storage' - -/* -new -if current workspace is empty, use it -if not, open a new folder then start -*/ - -// async function continueTutorial() { -// // TODO: verify that tutorial is loaded in workspace -// // TODO: verify progress -// // TODO: verify setup -// await loadProgressPosition() -// await openReadme() -// } - -async function newTutorial(tutorial: CR.Tutorial) { - // if workspace isn't empty, clear it out if given permission - const isEmpty: boolean = await isEmptyWorkspace() - if (!isEmpty) { - // eslint-disable-next-line - const options = ['Open a new folder', 'I\'ll clear the files and folders myself'] - const shouldOpenFolder = await vscode.window.showQuickPick(options) - if (shouldOpenFolder === options[0]) { - await vscode.commands.executeCommand('vscode.openFolder') - } - } - - await tutorialSetup(tutorial) - await openReadme() -} - - -export default async function start(context: vscode.ExtensionContext): Promise { - console.log('start', context) - - - return; - - // const modes = ['New'] - - // const canContinue = await validateCanContinue() - // if (canContinue) { - // modes.push('Continue') - // } - - // const selectedMode: string | undefined = await vscode.window.showQuickPick(modes) - - // if (!selectedMode) { - // throw new Error('No mode selected') - // return - // } - - // interface TutorialQuickPickItem extends vscode.QuickPickItem { - // id: string - // } - - // // load tutorial summaries - // const tutorialsData: { [id: string]: CR.TutorialSummary } = await api({ - // resource: 'getTutorialsSummary', - // }) - // const selectableTutorials: TutorialQuickPickItem[] = Object.keys(tutorialsData).map(id => { - // const tutorial = tutorialsData[id] - // return { - // label: tutorial.title, - // description: tutorial.description, - // // detail: '', // optional additional info - // id, - // } - // }) - // const selectedTutorial: TutorialQuickPickItem | undefined = await vscode.window.showQuickPick(selectableTutorials) - - // if (!selectedTutorial) { - // throw new Error('No tutorial selected') - // } - - // // load specific tutorial - // const tutorial: CR.Tutorial | undefined = await fetch({ - // resource: 'getTutorial', - // params: { id: selectedTutorial.id }, - // }) - - // if (!tutorial) { - // throw new Error('No tutorial found') - // } - - // switch (selectedMode) { - // // new tutorial - // case modes[0]: - // await newTutorial(tutorial) - // break - // // continue - // case modes[1]: - // await continueTutorial() - // break - // } - - // // setup hook to run tests on save - - - // TODO: start -} diff --git a/src/editor/index.ts b/src/editor/index.ts index 26504478..57f97bca 100644 --- a/src/editor/index.ts +++ b/src/editor/index.ts @@ -3,6 +3,7 @@ import * as CR from 'typings' import { createCommands } from './commands' import * as storage from '../services/storage' import * as git from '../services/git' +import * as position from '../services/position' interface Props { machine: CR.StateMachine, @@ -33,6 +34,7 @@ class Editor { machine: this.machine, storage, git, + position, }) for (const cmd in commands) { const command: vscode.Disposable = vscode.commands.registerCommand(cmd, commands[cmd]) diff --git a/src/services/api/index.ts b/src/services/api/index.ts index 206ae0eb..efa759dd 100644 --- a/src/services/api/index.ts +++ b/src/services/api/index.ts @@ -1,7 +1,7 @@ import * as CR from 'typings' // temporary tutorials -import basicTutorial from '../../state/context/tutorials/basic' +import basicTutorial from 'tutorials/basic' interface Options { resource: string diff --git a/src/state/actions/index.ts b/src/state/actions/index.ts index c2abb20b..dd3c4cfa 100644 --- a/src/state/actions/index.ts +++ b/src/state/actions/index.ts @@ -1,6 +1,11 @@ import { assign } from 'xstate' +// NOTE: codesmell - importing machine +import { machine } from '../../extension' +import api from '../../services/api' import * as CR from 'typings' import * as vscode from 'vscode' +import * as storage from '../../services/storage' +import * as git from '../../services/git' let initialTutorial: CR.Tutorial | undefined let initialProgress: CR.Progress = { @@ -11,7 +16,34 @@ let initialProgress: CR.Progress = { } export default { - tutorialLoad: assign({ + createWebview() { + console.log('execute coderoad.open_webview') + vscode.commands.executeCommand('coderoad.open_webview') + }, + async newOrContinue() { + // verify that the user has a tutorial & progress + // verify git is setup with a coderoad remote + const [tutorial, progress, hasGit, hasGitRemote] = await Promise.all([ + storage.getTutorial(), + storage.getProgress(), + git.gitVersion(), + git.gitCheckRemoteExists(), + ]) + const canContinue = !!(tutorial && progress && hasGit && hasGitRemote) + + if (canContinue) { + initialTutorial = tutorial + initialProgress = progress + } + + machine.send(canContinue ? 'CONTINUE' : 'NEW') + }, + async tutorialLaunch() { + // TODO: add selection of tutorial id + const tutorial: CR.Tutorial = await api({ resource: 'getTutorial', params: { id: '1' } }) + vscode.commands.executeCommand('coderoad.tutorial_launch', tutorial) + }, + tutorialContinue: assign({ // load initial data, progress & position data(): CR.TutorialData { console.log('ACTION: tutorialLoad.data') @@ -45,11 +77,4 @@ export default { return position } }), - createWebview() { - console.log('execute coderoad.open_webview') - vscode.commands.executeCommand('coderoad.open_webview') - }, - newOrContinue() { - vscode.commands.executeCommand('coderoad.new_or_continue') - } } \ No newline at end of file diff --git a/src/state/context/index.ts b/src/state/context/index.ts index 3b0443a4..36c3e65e 100644 --- a/src/state/context/index.ts +++ b/src/state/context/index.ts @@ -1,4 +1,4 @@ -import basicTutorialData from './tutorials/basic' +import basicTutorialData from 'tutorials/basic' import * as CR from 'typings' const tutorialContext: CR.MachineContext = { diff --git a/src/state/index.ts b/src/state/index.ts index 6a7cb3bb..1dc28042 100644 --- a/src/state/index.ts +++ b/src/state/index.ts @@ -36,12 +36,6 @@ class StateMachine { this.service.stop() } send(action: string | CR.Action) { - console.log('machine.send') - console.log(action) - this.service.send(action) - } - onReceive(action: string | CR.Action) { - console.log(action) this.service.send(action) } } diff --git a/src/state/machine.ts b/src/state/machine.ts index 273a915e..6a527c2f 100644 --- a/src/state/machine.ts +++ b/src/state/machine.ts @@ -11,7 +11,7 @@ export const machine = Machine< CR.MachineEvent >( { - id: 'tutorial', + id: 'root', context: initialContext, initial: 'SelectTutorial', states: { @@ -40,28 +40,28 @@ export const machine = Machine< }, }, InitializeTutorial: { + onEntry: ['tutorialLaunch'], on: { - TUTORIAL_LOADED: 'Tutorial' + TUTORIAL_LOADED: '#tutorial' } }, } - }, ContinueTutorial: { - onEntry: 'tutorialLoad', + onEntry: ['tutorialContinue'], on: { - TUTORIAL_START: { - target: 'Tutorial.LoadNext', - } + TUTORIAL_START: '#tutorial-load-next' } }, } }, Tutorial: { + id: 'tutorial', initial: 'Summary', states: { LoadNext: { - onEntry: () => send('LOAD_NEXT'), + id: 'tutorial-load-next', + // onEntry: [() => send('LOAD_NEXT')], on: { LOAD_NEXT: [ { @@ -144,7 +144,7 @@ export const machine = Machine< cond: 'hasNextLevel', }, { - target: 'EndTutorial', + target: '#root.Tutorial.EndTutorial', }, ], }, diff --git a/src/state/message.ts b/src/state/message.ts deleted file mode 100644 index 571b2100..00000000 --- a/src/state/message.ts +++ /dev/null @@ -1,13 +0,0 @@ -// import panel from '../views/Panel' -import * as CR from 'typings' - -export const onSend = (action: CR.Action) => { - // if (!panel || !panel.currentPanel) { - // throw new Error('No valid panel available') - // } - // panel.currentPanel.postMessage(action) -} - -export const onReceive = (action: CR.Action) => { - console.log(`RECEIVED: ${JSON.stringify(action)}`) -} diff --git a/tsconfig.json b/tsconfig.json index cef79727..dd77dd55 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,7 @@ "dom" ], "sourceMap": true, - "rootDir": "src", + "rootDirs": ["src", "tutorials"], "baseUrl": "src", "strict": true, /* enable all strict type-checking options */ /* Additional Checks */ @@ -23,6 +23,9 @@ "emitDecoratorMetadata": true, "paths": { "typings": ["../typings/index.d.ts"], + "tutorials/basic": [ + "../tutorials/basic.ts" + ] }, }, "exclude": [ diff --git a/src/state/context/tutorials/basic.ts b/tutorials/basic.ts similarity index 96% rename from src/state/context/tutorials/basic.ts rename to tutorials/basic.ts index 395ac008..81b2c9e6 100644 --- a/src/state/context/tutorials/basic.ts +++ b/tutorials/basic.ts @@ -4,7 +4,7 @@ const basic: CR.Tutorial = { id: 'tutorialId', meta: { version: '0.1.0', - repo: 'https://github.com/ShMcK/coderoad-vscode.git', + repo: 'https://github.com/ShMcK/coderoad-tutorial-basic.git', createdBy: 'shmck', createdAt: 'Sat, 11 May 2019 18:25:24 GMT', updatedBy: 'shmck', diff --git a/typings/index.d.ts b/typings/index.d.ts index 044be525..f5771d1a 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -168,5 +168,4 @@ export interface StateMachine { activate(): void deactivate(): void send(action: string | Action): void - onReceive(action: string | Action): void } diff --git a/web-app/.prettierrc.js b/web-app/.prettierrc.js new file mode 100644 index 00000000..aee180bd --- /dev/null +++ b/web-app/.prettierrc.js @@ -0,0 +1,7 @@ +module.exports = { + printWidth: 120, + semi: false, + singleQuote: true, + tabWidth: 2, + trailingComma: 'all', +} diff --git a/web-app/src/App.tsx b/web-app/src/App.tsx index 22e7e0a2..c0e40849 100644 --- a/web-app/src/App.tsx +++ b/web-app/src/App.tsx @@ -3,73 +3,54 @@ import * as CR from 'typings' import Debugger from './components/Debugger' import Routes from './Routes' +import DataContext, { initialState, initialData } from './utils/DataContext' interface ReceivedEvent { - data: CR.Action + data: CR.Action } -const initialState = { SelectTutorial: 'Initial '} -const initialData: CR.MachineContext = { - position: { levelId: '', stageId: '', stepId: '' }, - data: { - summary: { - title: '', - description: '', - levelList: [], - }, - levels: {}, - stages: {}, - steps: {}, - }, - progress: { levels: {}, stages: {}, steps: {}, complete: false }, -} - -const DataContext = React.createContext({ state: initialState, ...initialData }) - - - const App = () => { - const [state, setState] = React.useState(initialState) - const [data, setData]: [CR.MachineContext, (data: CR.MachineContext) => void] = React.useState(initialData) - - const handleEvent = (event: ReceivedEvent): void => { - const message = event.data - console.log('RECEIVED') - console.log(message) - // messages from core - if (message.type === 'SET_STATE') { - setState(message.payload.state) - setData(message.payload.data) - } else if (message.type === 'SET_DATA') { - setData(message.payload.data) - } + const [state, setState] = React.useState(initialState) + const [data, setData]: [CR.MachineContext, (data: CR.MachineContext) => void] = React.useState(initialData) + + const handleEvent = (event: ReceivedEvent): void => { + const message = event.data + console.log('RECEIVED') + console.log(message) + // messages from core + if (message.type === 'SET_STATE') { + setState(message.payload.state) + setData(message.payload.data) + } else if (message.type === 'SET_DATA') { + setData(message.payload.data) } - - // event bus listener - React.useEffect(() => { - const listener = 'message' - window.addEventListener(listener, handleEvent) - return () => { - window.removeEventListener(listener, handleEvent) - } - }) - - const value = { - state, - position: data.position, - data: data.data, - progress: data.progress, + } + + // event bus listener + React.useEffect(() => { + const listener = 'message' + window.addEventListener(listener, handleEvent) + return () => { + window.removeEventListener(listener, handleEvent) } - - // TODO: refactor cond to user and accept first route as if/else if - return ( - -
- - -
-
- ) + }) + + const value = { + state, + position: data.position, + data: data.data, + progress: data.progress, + } + + // TODO: refactor cond to user and accept first route as if/else if + return ( + +
+ + +
+
+ ) } -export default App \ No newline at end of file +export default App diff --git a/web-app/src/Routes.tsx b/web-app/src/Routes.tsx index 044cd066..c00c56b8 100644 --- a/web-app/src/Routes.tsx +++ b/web-app/src/Routes.tsx @@ -1,27 +1,33 @@ import * as React from 'react' -import { send } from './utils/vscode' +import Loading from './components/Loading' import Cond from './components/Cond' import NewPage from './containers/New' import ContinuePage from './containers/Continue' - +import TutorialPage from './containers/Tutorial' interface Props { state: any } const Routes = ({ state }: Props) => { - // TODO: refactor cond to user and accept first route as if/else if - return ( -
- - send('TUTORIAL_START')} /> - - - console.log('continue!')} tutorials={[]} /> - -
- ) + // TODO: refactor cond to user and accept first route as if/else if + return ( +
+ + + + + + + + + + + + +
+ ) } -export default Routes \ No newline at end of file +export default Routes diff --git a/web-app/src/components/Cond/utils/state.ts b/web-app/src/components/Cond/utils/state.ts index 5706cc57..fdf4abf5 100644 --- a/web-app/src/components/Cond/utils/state.ts +++ b/web-app/src/components/Cond/utils/state.ts @@ -1,9 +1,16 @@ export function stateMatch(state: any, statePath: string) { let current = state let paths = statePath.split('.') + let complete = false try { for (const p of paths) { - current = current[p] + if (p === current && !complete) { + // handle strings + complete = true + } else { + // handle objects + current = current[p] + } } } catch (error) { return false diff --git a/web-app/src/components/Loading/index.tsx b/web-app/src/components/Loading/index.tsx index 9fd7fb7f..02955315 100644 --- a/web-app/src/components/Loading/index.tsx +++ b/web-app/src/components/Loading/index.tsx @@ -1,11 +1,6 @@ import * as React from 'react' -// import send from '../../data/send' const Loading = () => { - React.useEffect(() => { - // load tutorial and progress on startup - // send({ type: 'GET/TUTORIAL' }) - }) return
Loading...
} diff --git a/web-app/src/containers/Continue/index.tsx b/web-app/src/containers/Continue/index.tsx index 128ed1f7..c40ff78b 100644 --- a/web-app/src/containers/Continue/index.tsx +++ b/web-app/src/containers/Continue/index.tsx @@ -1,28 +1,33 @@ import * as React from 'react' -import CR from 'typings' - -import ContinueItem from './ContinueItem' +import { send } from '../../utils/vscode' +import DataContext from '../../utils/DataContext' +import { Button, Card } from '@alifd/next' interface Props { - tutorials: CR.Tutorial[] - onContinue(tutorialId: string): void - // onReset(): void + onContinue(): void } -const ContinuePage = (props: Props) => { +export const ContinuePage = (props: Props) => { // context + const { data } = React.useContext(DataContext) return (
- {props.tutorials.map((tutorial: CR.Tutorial) => ( - props.onContinue(tutorial.id)} - title={tutorial.data.summary.title} - description={tutorial.data.summary.description} - /> - ))} +

Continue

+ +
+

{data.summary.title}

+

{data.summary.description}

+ +
+
) } -export default ContinuePage +export default () => ( + { + send('TUTORIAL_START') + }} + /> +) diff --git a/web-app/src/containers/Continue/ContinueItem.tsx b/web-app/src/containers/New/TutorialItem.tsx similarity index 85% rename from web-app/src/containers/Continue/ContinueItem.tsx rename to web-app/src/containers/New/TutorialItem.tsx index 278206a9..6138e706 100644 --- a/web-app/src/containers/Continue/ContinueItem.tsx +++ b/web-app/src/containers/New/TutorialItem.tsx @@ -7,7 +7,7 @@ interface Props { onContinue(): void } -const ContinueItem = (props: Props) => { +const TutorialItem = (props: Props) => { return (
@@ -19,4 +19,4 @@ const ContinueItem = (props: Props) => { ) } -export default ContinueItem +export default TutorialItem diff --git a/web-app/src/containers/New/index.tsx b/web-app/src/containers/New/index.tsx index 35010d3d..a8e46992 100644 --- a/web-app/src/containers/New/index.tsx +++ b/web-app/src/containers/New/index.tsx @@ -1,18 +1,36 @@ import * as React from 'react' import { Button } from '@alifd/next' +import Cond from '../../components/Cond' +import DataContext from '../../utils/DataContext' +import { send } from '../../utils/vscode' interface Props { onNew(tutorialId: string): void } -const NewPage = (props: Props) => { +export const NewPage = (props: Props) => { + const { state } = React.useContext(DataContext) + const [tutorialList, setTutorialList] = React.useState([{ id: '1', title: 'Demo', description: 'A basic demo' }]) // context return (
-

Start a new Project

- + +
+

Start a new Project

+ {tutorialList.map(tutorial => ( +
+

{tutorial.title}

+

{tutorial.description}

+ +
+ ))} +
+
+ +
Initializing tutorial...
+
) } -export default NewPage +export default () => send('TUTORIAL_START')} /> diff --git a/web-app/src/containers/Tutorial/index.tsx b/web-app/src/containers/Tutorial/index.tsx new file mode 100644 index 00000000..c7d75d5d --- /dev/null +++ b/web-app/src/containers/Tutorial/index.tsx @@ -0,0 +1,14 @@ +import * as React from 'react' + +interface Props {} + +const Tutorial = (props: Props) => { + // useContext + return ( +
+

Tutorial

+
+ ) +} + +export default Tutorial diff --git a/src/services/api/fetch.ts b/web-app/src/services/api/index.tsx similarity index 92% rename from src/services/api/fetch.ts rename to web-app/src/services/api/index.tsx index 206ae0eb..efa759dd 100644 --- a/src/services/api/fetch.ts +++ b/web-app/src/services/api/index.tsx @@ -1,7 +1,7 @@ import * as CR from 'typings' // temporary tutorials -import basicTutorial from '../../state/context/tutorials/basic' +import basicTutorial from 'tutorials/basic' interface Options { resource: string diff --git a/web-app/src/utils/DataContext.tsx b/web-app/src/utils/DataContext.tsx new file mode 100644 index 00000000..ffee2cfc --- /dev/null +++ b/web-app/src/utils/DataContext.tsx @@ -0,0 +1,22 @@ +import * as React from 'react' +import * as CR from 'typings' + +export const initialState = { SelectTutorial: 'Initial ' } +export const initialData: CR.MachineContext = { + position: { levelId: '', stageId: '', stepId: '' }, + data: { + summary: { + title: '', + description: '', + levelList: [], + }, + levels: {}, + stages: {}, + steps: {}, + }, + progress: { levels: {}, stages: {}, steps: {}, complete: false }, +} + +const DataContext = React.createContext({ state: initialState, ...initialData }) + +export default DataContext diff --git a/web-app/stories/Continue.stories.tsx b/web-app/stories/Continue.stories.tsx index 1f4df9d0..bfa1b65c 100644 --- a/web-app/stories/Continue.stories.tsx +++ b/web-app/stories/Continue.stories.tsx @@ -3,7 +3,7 @@ import React from 'react' import { storiesOf } from '@storybook/react' import { action } from '@storybook/addon-actions' -import Continue from '../src/containers/Continue' +import { ContinuePage } from '../src/containers/Continue' import demo from './data/basic' -storiesOf('Continue', module).add('Page', () => ) +storiesOf('Continue', module).add('Page', () => ) diff --git a/web-app/stories/New.stories.tsx b/web-app/stories/New.stories.tsx index 5ade6701..f82ef46e 100644 --- a/web-app/stories/New.stories.tsx +++ b/web-app/stories/New.stories.tsx @@ -3,6 +3,6 @@ import React from 'react' import { storiesOf } from '@storybook/react' import { action } from '@storybook/addon-actions' -import New from '../src/containers/New' +import { NewPage } from '../src/containers/New' -storiesOf('New', module).add('Page', () => ) +storiesOf('New', module).add('Page', () => ) diff --git a/web-app/tsconfig.paths.json b/web-app/tsconfig.paths.json index 89a64aa3..12e7166b 100644 --- a/web-app/tsconfig.paths.json +++ b/web-app/tsconfig.paths.json @@ -9,6 +9,9 @@ "typings": [ "../../typings/index.d.ts" ], + "tutorials/basic": [ + "../../tutorials/basic.ts" + ] } }, "exclude": [