mirror of
https://github.com/linux-odyssey/linux-odyssey.git
synced 2026-11-05 19:20:12 +08:00
refactor: replace "folder" with "directory" across file handling components and tests
This commit is contained in:
1 parent
7e50c8e06c
commit
cdd22634e0
18 files changed
+149
-103
No files matched your search
@@ -22,8 +22,8 @@ function nodeClass() {
|
|||||||
<template>
|
<template>
|
||||||
<a href="#" :class="nodeClass()">
|
<a href="#" :class="nodeClass()">
|
||||||
<font-awesome-icon
|
<font-awesome-icon
|
||||||
v-if="node.type === 'folder'"
|
v-if="node.type === 'directory'"
|
||||||
:icon="['far', 'folder']"
|
:icon="['far', 'directory']"
|
||||||
class="text-text-primary size-8"
|
class="text-text-primary size-8"
|
||||||
/>
|
/>
|
||||||
<font-awesome-icon
|
<font-awesome-icon
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ async function collectFilesInfo(inputPath, level = 0, hiddenFiles = false) {
|
|||||||
{
|
{
|
||||||
path: inputPath,
|
path: inputPath,
|
||||||
name,
|
name,
|
||||||
type: stats.isDirectory() ? 'folder' : 'file',
|
type: stats.isDirectory() ? 'directory' : 'file',
|
||||||
discovered,
|
discovered,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ import { basename } from './utils.js'
|
|||||||
|
|
||||||
export interface FileObject {
|
export interface FileObject {
|
||||||
path: string
|
path: string
|
||||||
type: string
|
type: 'file' | 'directory'
|
||||||
discovered: boolean
|
discovered: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class File implements FileObject {
|
export default class File implements FileObject {
|
||||||
path: string
|
path: string
|
||||||
name: string
|
name: string
|
||||||
type: string
|
type: 'file' | 'directory'
|
||||||
discovered: boolean
|
discovered: boolean
|
||||||
|
|
||||||
constructor(obj: FileObject) {
|
constructor(obj: FileObject) {
|
||||||
@@ -20,7 +20,7 @@ export default class File implements FileObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isDirectory() {
|
isDirectory() {
|
||||||
return this.type === 'folder'
|
return this.type === 'directory'
|
||||||
}
|
}
|
||||||
|
|
||||||
contains(file: FileObject) {
|
contains(file: FileObject) {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export default class FileGraph extends FileNode {
|
|||||||
|
|
||||||
remove(files: FileObject[]) {
|
remove(files: FileObject[]) {
|
||||||
// Logic to remove a file node from the graph
|
// Logic to remove a file node from the graph
|
||||||
// If the file node is a folder, also remove all children
|
// If the file node is a directory, also remove all children
|
||||||
// If the file node doesn't exist, throw an error
|
// If the file node doesn't exist, throw an error
|
||||||
|
|
||||||
files.forEach((file) => {
|
files.forEach((file) => {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export default class FileNode extends File {
|
|||||||
} else if (makeParents) {
|
} else if (makeParents) {
|
||||||
const parent = new FileNode({
|
const parent = new FileNode({
|
||||||
path: parentPath,
|
path: parentPath,
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: false,
|
discovered: false,
|
||||||
children: [file],
|
children: [file],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import File from '../src/file.js'
|
|||||||
describe('File', () => {
|
describe('File', () => {
|
||||||
let file: File
|
let file: File
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
file = new File({ path: '/home/user', type: 'folder', discovered: true })
|
file = new File({ path: '/home/user', type: 'directory', discovered: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
const filesToCompare = [
|
const filesToCompare = [
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { describe, test, expect } from '@jest/globals'
|
import { describe, test, expect } from '@jest/globals'
|
||||||
import FileGraph from '../src/fileGraph.js'
|
import FileGraph from '../src/fileGraph.js'
|
||||||
import FileNode from '../src/fileNode.js'
|
import FileNode from '../src/fileNode.js'
|
||||||
|
import { FileObject } from '../src/file.js'
|
||||||
|
|
||||||
describe('FileGraph', () => {
|
describe('FileGraph', () => {
|
||||||
let root: FileNode
|
let root: FileNode
|
||||||
@@ -8,7 +9,7 @@ describe('FileGraph', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
root = new FileNode({
|
root = new FileNode({
|
||||||
path: '/',
|
path: '/',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -16,7 +17,7 @@ describe('FileGraph', () => {
|
|||||||
test('add files to graph', () => {
|
test('add files to graph', () => {
|
||||||
const fileGraph = new FileGraph(root)
|
const fileGraph = new FileGraph(root)
|
||||||
|
|
||||||
const filesToAdd = [
|
const filesToAdd: FileObject[] = [
|
||||||
{
|
{
|
||||||
path: '/home/user/file1.txt',
|
path: '/home/user/file1.txt',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
@@ -24,7 +25,7 @@ describe('FileGraph', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/home/user/folder1',
|
path: '/home/user/folder1',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@@ -41,12 +42,12 @@ describe('FileGraph', () => {
|
|||||||
const filesToAdd = [
|
const filesToAdd = [
|
||||||
{
|
{
|
||||||
path: '/home/user/folder1',
|
path: '/home/user/folder1',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/home/user/folder2',
|
path: '/home/user/folder2',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -61,22 +62,22 @@ describe('FileGraph', () => {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
fileGraph.add(filesToAdd)
|
fileGraph.add(filesToAdd as FileObject[])
|
||||||
|
|
||||||
const filesToRemove = [
|
const filesToRemove = [
|
||||||
{
|
{
|
||||||
path: '/home/user/folder1/file1.txt',
|
path: '/home/user/folder1/file1.txt',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/home/user/folder2',
|
path: '/home/user/folder2',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
fileGraph.remove(filesToRemove)
|
fileGraph.remove(filesToRemove as FileObject[])
|
||||||
|
|
||||||
expect(fileGraph.children[0].children[0].children.length).toBe(1)
|
expect(fileGraph.children[0].children[0].children.length).toBe(1)
|
||||||
expect(fileGraph.children[0].children[0].children[0].name).toBe('folder1')
|
expect(fileGraph.children[0].children[0].children[0].name).toBe('folder1')
|
||||||
@@ -90,7 +91,7 @@ describe('FileGraph', () => {
|
|||||||
const discoverFiles = [
|
const discoverFiles = [
|
||||||
{
|
{
|
||||||
path: '/home/user/folder1',
|
path: '/home/user/folder1',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -110,7 +111,7 @@ describe('FileGraph', () => {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
fileGraph.discover(discoverFiles)
|
fileGraph.discover(discoverFiles as FileObject[])
|
||||||
expect(fileGraph.children[0].children[0].children[0].children.length).toBe(
|
expect(fileGraph.children[0].children[0].children[0].children.length).toBe(
|
||||||
3
|
3
|
||||||
)
|
)
|
||||||
@@ -122,12 +123,12 @@ describe('FileGraph', () => {
|
|||||||
const filesToAdd = [
|
const filesToAdd = [
|
||||||
{
|
{
|
||||||
path: '/home/user/folder1',
|
path: '/home/user/folder1',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/home/user/folder2',
|
path: '/home/user/folder2',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -142,12 +143,12 @@ describe('FileGraph', () => {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
fileGraph.add(filesToAdd)
|
fileGraph.add(filesToAdd as FileObject[])
|
||||||
|
|
||||||
const newFiles = [
|
const newFiles = [
|
||||||
{
|
{
|
||||||
path: '/home/user/folder1',
|
path: '/home/user/folder1',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -167,7 +168,7 @@ describe('FileGraph', () => {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
fileGraph.discover(newFiles)
|
fileGraph.discover(newFiles as FileObject[])
|
||||||
|
|
||||||
expect(fileGraph.children[0].children[0].children.length).toBe(2)
|
expect(fileGraph.children[0].children[0].children.length).toBe(2)
|
||||||
expect(fileGraph.children[0].children[0].children[0].children.length).toBe(
|
expect(fileGraph.children[0].children[0].children[0].children.length).toBe(
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ describe('FileNode', () => {
|
|||||||
rootNode = new FileNode({
|
rootNode = new FileNode({
|
||||||
path: '/home/rudeus',
|
path: '/home/rudeus',
|
||||||
name: 'rudeus',
|
name: 'rudeus',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -51,7 +51,7 @@ describe('FileNode', () => {
|
|||||||
test('should throw error when adding duplicate item', () => {
|
test('should throw error when adding duplicate item', () => {
|
||||||
const child = {
|
const child = {
|
||||||
path: '/home/rudeus/test',
|
path: '/home/rudeus/test',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ describe('FileNode', () => {
|
|||||||
const child = {
|
const child = {
|
||||||
path: '/home/rudeus/alphabet/test',
|
path: '/home/rudeus/alphabet/test',
|
||||||
name: 'test',
|
name: 'test',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ describe('FileNode', () => {
|
|||||||
{
|
{
|
||||||
path: '/home/rudeus/test',
|
path: '/home/rudeus/test',
|
||||||
name: 'test',
|
name: 'test',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -131,21 +131,21 @@ describe('FileNode', () => {
|
|||||||
test('merge-subtree', () => {
|
test('merge-subtree', () => {
|
||||||
const root = {
|
const root = {
|
||||||
path: '/',
|
path: '/',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
}
|
}
|
||||||
const files1 = [
|
const files1 = [
|
||||||
root,
|
root,
|
||||||
{ path: '/a', type: 'file', discovered: true },
|
{ path: '/a', type: 'file', discovered: true },
|
||||||
{ path: '/b', type: 'folder', discovered: true },
|
{ path: '/b', type: 'directory', discovered: true },
|
||||||
{ path: '/b/c', type: 'folder', discovered: true },
|
{ path: '/b/c', type: 'directory', discovered: true },
|
||||||
{ path: '/b/d', type: 'file', discovered: true },
|
{ path: '/b/d', type: 'file', discovered: true },
|
||||||
{ path: '/b/c/f', type: 'file', discovered: true },
|
{ path: '/b/c/f', type: 'file', discovered: true },
|
||||||
]
|
]
|
||||||
|
|
||||||
const files2 = [
|
const files2 = [
|
||||||
{ path: '/b', type: 'folder', discovered: true },
|
{ path: '/b', type: 'directory', discovered: true },
|
||||||
{ path: '/b/c', type: 'folder', discovered: true },
|
{ path: '/b/c', type: 'directory', discovered: true },
|
||||||
{ path: '/b/e', type: 'file', discovered: true },
|
{ path: '/b/e', type: 'file', discovered: true },
|
||||||
{ path: '/b/c/g', type: 'file', discovered: true },
|
{ path: '/b/c/g', type: 'file', discovered: true },
|
||||||
]
|
]
|
||||||
@@ -153,8 +153,8 @@ describe('FileNode', () => {
|
|||||||
const expected = [
|
const expected = [
|
||||||
root,
|
root,
|
||||||
{ path: '/a', type: 'file', discovered: true },
|
{ path: '/a', type: 'file', discovered: true },
|
||||||
{ path: '/b', type: 'folder', discovered: true },
|
{ path: '/b', type: 'directory', discovered: true },
|
||||||
{ path: '/b/c', type: 'folder', discovered: true },
|
{ path: '/b/c', type: 'directory', discovered: true },
|
||||||
{ path: '/b/e', type: 'file', discovered: true },
|
{ path: '/b/e', type: 'file', discovered: true },
|
||||||
{ path: '/b/c/g', type: 'file', discovered: true },
|
{ path: '/b/c/g', type: 'file', discovered: true },
|
||||||
]
|
]
|
||||||
@@ -168,29 +168,29 @@ describe('FileNode', () => {
|
|||||||
// The original tree should keep the children that are not in the node to merge
|
// The original tree should keep the children that are not in the node to merge
|
||||||
const root = {
|
const root = {
|
||||||
path: '/',
|
path: '/',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
}
|
}
|
||||||
const files1 = [
|
const files1 = [
|
||||||
root,
|
root,
|
||||||
{ path: '/a', type: 'file', discovered: true },
|
{ path: '/a', type: 'file', discovered: true },
|
||||||
{ path: '/b', type: 'folder', discovered: true },
|
{ path: '/b', type: 'directory', discovered: true },
|
||||||
{ path: '/b/c', type: 'folder', discovered: true },
|
{ path: '/b/c', type: 'directory', discovered: true },
|
||||||
{ path: '/b/d', type: 'file', discovered: true },
|
{ path: '/b/d', type: 'file', discovered: true },
|
||||||
{ path: '/b/c/f', type: 'file', discovered: true },
|
{ path: '/b/c/f', type: 'file', discovered: true },
|
||||||
]
|
]
|
||||||
|
|
||||||
const files2 = [
|
const files2 = [
|
||||||
{ path: '/b', type: 'folder', discovered: true },
|
{ path: '/b', type: 'directory', discovered: true },
|
||||||
{ path: '/b/c', type: 'folder', discovered: true },
|
{ path: '/b/c', type: 'directory', discovered: true },
|
||||||
{ path: '/b/e', type: 'file', discovered: true },
|
{ path: '/b/e', type: 'file', discovered: true },
|
||||||
]
|
]
|
||||||
|
|
||||||
const expected = [
|
const expected = [
|
||||||
root,
|
root,
|
||||||
{ path: '/a', type: 'file', discovered: true },
|
{ path: '/a', type: 'file', discovered: true },
|
||||||
{ path: '/b', type: 'folder', discovered: true },
|
{ path: '/b', type: 'directory', discovered: true },
|
||||||
{ path: '/b/c', type: 'folder', discovered: true },
|
{ path: '/b/c', type: 'directory', discovered: true },
|
||||||
{ path: '/b/e', type: 'file', discovered: true },
|
{ path: '/b/e', type: 'file', discovered: true },
|
||||||
{ path: '/b/c/f', type: 'file', discovered: true },
|
{ path: '/b/c/f', type: 'file', discovered: true },
|
||||||
]
|
]
|
||||||
@@ -201,19 +201,19 @@ describe('FileNode', () => {
|
|||||||
test('merge-hidden', () => {
|
test('merge-hidden', () => {
|
||||||
const root = {
|
const root = {
|
||||||
path: '/',
|
path: '/',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
}
|
}
|
||||||
const files1 = [
|
const files1 = [
|
||||||
root,
|
root,
|
||||||
{ path: '/a', type: 'file', discovered: true },
|
{ path: '/a', type: 'file', discovered: true },
|
||||||
{ path: '/.b', type: 'file', discovered: false },
|
{ path: '/.b', type: 'file', discovered: false },
|
||||||
{ path: '/c', type: 'folder', discovered: true },
|
{ path: '/c', type: 'directory', discovered: true },
|
||||||
{ path: '/c/.d', type: 'file', discovered: false },
|
{ path: '/c/.d', type: 'file', discovered: false },
|
||||||
]
|
]
|
||||||
|
|
||||||
const files2 = [
|
const files2 = [
|
||||||
{ path: '/c', type: 'folder', discovered: true },
|
{ path: '/c', type: 'directory', discovered: true },
|
||||||
{ path: '/c/.d', type: 'file', discovered: true },
|
{ path: '/c/.d', type: 'file', discovered: true },
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -221,7 +221,7 @@ describe('FileNode', () => {
|
|||||||
root,
|
root,
|
||||||
{ path: '/a', type: 'file', discovered: true },
|
{ path: '/a', type: 'file', discovered: true },
|
||||||
{ path: '/.b', type: 'file', discovered: false },
|
{ path: '/.b', type: 'file', discovered: false },
|
||||||
{ path: '/c', type: 'folder', discovered: true },
|
{ path: '/c', type: 'directory', discovered: true },
|
||||||
{ path: '/c/.d', type: 'file', discovered: true },
|
{ path: '/c/.d', type: 'file', discovered: true },
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -231,24 +231,24 @@ describe('FileNode', () => {
|
|||||||
test('remove-child', () => {
|
test('remove-child', () => {
|
||||||
const root = {
|
const root = {
|
||||||
path: '/',
|
path: '/',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
}
|
}
|
||||||
const files = [
|
const files = [
|
||||||
root,
|
root,
|
||||||
{ path: '/a', type: 'file', discovered: true },
|
{ path: '/a', type: 'file', discovered: true },
|
||||||
{ path: '/b', type: 'folder', discovered: true },
|
{ path: '/b', type: 'directory', discovered: true },
|
||||||
{ path: '/b/c', type: 'folder', discovered: true },
|
{ path: '/b/c', type: 'directory', discovered: true },
|
||||||
{ path: '/b/d', type: 'file', discovered: true },
|
{ path: '/b/d', type: 'file', discovered: true },
|
||||||
{ path: '/b/c/f', type: 'file', discovered: true },
|
{ path: '/b/c/f', type: 'file', discovered: true },
|
||||||
]
|
]
|
||||||
|
|
||||||
const fileToRemove = { path: '/b/c', type: 'folder', discovered: true }
|
const fileToRemove = { path: '/b/c', type: 'directory', discovered: true }
|
||||||
|
|
||||||
const expected = [
|
const expected = [
|
||||||
root,
|
root,
|
||||||
{ path: '/a', type: 'file', discovered: true },
|
{ path: '/a', type: 'file', discovered: true },
|
||||||
{ path: '/b', type: 'folder', discovered: true },
|
{ path: '/b', type: 'directory', discovered: true },
|
||||||
{ path: '/b/d', type: 'file', discovered: true },
|
{ path: '/b/d', type: 'file', discovered: true },
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -259,7 +259,7 @@ describe('FileNode', () => {
|
|||||||
test('load nested fileNode', () => {
|
test('load nested fileNode', () => {
|
||||||
const data = {
|
const data = {
|
||||||
path: '/',
|
path: '/',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
@@ -269,12 +269,12 @@ test('load nested fileNode', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/b',
|
path: '/b',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/b/c',
|
path: '/b/c',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
discovered: true,
|
discovered: true,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -61,20 +61,13 @@
|
|||||||
},
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": ["file", "directory"]
|
||||||
"file",
|
|
||||||
"folder"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"exists": {
|
"exists": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": ["path", "type", "exists"],
|
||||||
"path",
|
|
||||||
"type",
|
|
||||||
"exists"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -95,10 +88,7 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"type": {
|
"type": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": ["narrative", "dialogue"],
|
||||||
"narrative",
|
|
||||||
"dialogue"
|
|
||||||
],
|
|
||||||
"default": "narrative"
|
"default": "narrative"
|
||||||
},
|
},
|
||||||
"content": {
|
"content": {
|
||||||
@@ -108,9 +98,7 @@
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": ["content"],
|
||||||
"content"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"exceptions": {
|
"exceptions": {
|
||||||
@@ -132,20 +120,12 @@
|
|||||||
"default": false
|
"default": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": ["id", "condition", "response"],
|
||||||
"id",
|
|
||||||
"condition",
|
|
||||||
"response"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": ["id", "condition", "response"],
|
||||||
"id",
|
|
||||||
"condition",
|
|
||||||
"response"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -168,22 +148,12 @@
|
|||||||
"default": []
|
"default": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": ["id", "condition", "response"],
|
||||||
"id",
|
|
||||||
"condition",
|
|
||||||
"response"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": ["id", "title", "image", "instruction", "stages"],
|
||||||
"id",
|
|
||||||
"title",
|
|
||||||
"image",
|
|
||||||
"instruction",
|
|
||||||
"stages"
|
|
||||||
],
|
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@ import { z } from 'zod'
|
|||||||
|
|
||||||
export const fileInputSchema = z.object({
|
export const fileInputSchema = z.object({
|
||||||
path: z.string(),
|
path: z.string(),
|
||||||
type: z.enum(['file', 'folder']),
|
type: z.enum(['file', 'directory']),
|
||||||
})
|
})
|
||||||
|
|
||||||
export type IFileInput = z.infer<typeof fileInputSchema>
|
export type IFileInput = z.infer<typeof fileInputSchema>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export class MockFileChecker implements IFileExistenceChecker {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/home/user/Downloads',
|
path: '/home/user/Downloads',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ test('file matcher', async () => {
|
|||||||
await checkFiles(checker, [
|
await checkFiles(checker, [
|
||||||
{
|
{
|
||||||
path: '/home/user/Downloads',
|
path: '/home/user/Downloads',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
exists: true,
|
exists: true,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
@@ -71,7 +71,7 @@ test('file matcher', async () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/home/user/Downloads',
|
path: '/home/user/Downloads',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
exists: true,
|
exists: true,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export const Session = model<ISession>(
|
|||||||
type: nodeSchema,
|
type: nodeSchema,
|
||||||
default: {
|
default: {
|
||||||
path: '/',
|
path: '/',
|
||||||
type: 'folder',
|
type: 'directory',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export interface File {
|
|||||||
|
|
||||||
const fileTypes = new Map([
|
const fileTypes = new Map([
|
||||||
['file', '-f'],
|
['file', '-f'],
|
||||||
['folder', '-d'],
|
['directory', '-d'],
|
||||||
['link', '-L'],
|
['link', '-L'],
|
||||||
['socket', '-S'],
|
['socket', '-S'],
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ describe('buildFileCheckCmd function', () => {
|
|||||||
output: ['test', '-f', '/etc/passwd'],
|
output: ['test', '-f', '/etc/passwd'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: { path: '/etc', type: 'folder' },
|
input: { path: '/etc', type: 'directory' },
|
||||||
output: ['test', '-d', '/etc'],
|
output: ['test', '-d', '/etc'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ instruction: |
|
|||||||
在這個關卡中,你將會學到:
|
在這個關卡中,你將會學到:
|
||||||
1. 如何使用 `echo` 在終端機上顯示文字。
|
1. 如何使用 `echo` 在終端機上顯示文字。
|
||||||
|
|
||||||
# requirements:
|
requirements:
|
||||||
# - get-started
|
- get-started
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- id: start
|
- id: start
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
echo start
|
||||||
|
echo hello
|
||||||
|
echo next
|
||||||
|
echo finish
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
id: get-started
|
||||||
|
image: base
|
||||||
|
title: 終端機的基本知識
|
||||||
|
instruction: |
|
||||||
|
歡迎來到 Linux Odyssey!
|
||||||
|
在這個關卡中,你將會學到:
|
||||||
|
1. 如何使用這個網站
|
||||||
|
2. 如何使用終端機
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- id: start
|
||||||
|
name: 按下 `開始` 按鈕開始
|
||||||
|
condition:
|
||||||
|
command: ^echo start$
|
||||||
|
response:
|
||||||
|
content: |
|
||||||
|
在畫面的中下方的區塊是終端機,你可以在終端機裡輸入指令來操作電腦。
|
||||||
|
在終端機中,你所看到的 `commander:~ $` 叫做命令提示字串。
|
||||||
|
命令提示字串最後的錢字號 `$` 叫命令提示符,你必須將指令輸入在它的後方。
|
||||||
|
當你輸入完指令並按下 Enter 之後,終端機會執行你輸入的指令,並且會從終端機的下一行開始輸出指令的執行結果。
|
||||||
|
直到你看到新的 `commander:~ $` 出現時,代表你可以輸入下一個指令了。
|
||||||
|
現在請你輸入 `echo hello` 來看看會發生什麼事。
|
||||||
|
hint: |
|
||||||
|
在 `commander:~ $` 後面輸入 `echo hello` 並按下 Enter
|
||||||
|
|
||||||
|
- id: echo-hello
|
||||||
|
name: 在終端機輸入 `echo hello`
|
||||||
|
requirements:
|
||||||
|
- start
|
||||||
|
condition:
|
||||||
|
command: ^echo hello$
|
||||||
|
response:
|
||||||
|
content: |
|
||||||
|
你應該可以看到終端機上顯示了以下內容:
|
||||||
|
```
|
||||||
|
commander:~ $ echo hello
|
||||||
|
hello
|
||||||
|
```
|
||||||
|
第一行的 `commander:~ $` 是命令提示字串,接在後方的 `echo hello` 是你輸入的指令。
|
||||||
|
第二行的 `hello` 則是終端機的輸出結果。
|
||||||
|
你現在知道了如何在終端機上輸入指令,以及查看終端機輸出的位置。
|
||||||
|
現在請輸入 `echo next` 來繼續下一個步驟。
|
||||||
|
hint: |
|
||||||
|
輸入 `echo next` 繼續下一個步驟
|
||||||
|
|
||||||
|
- id: echo-next
|
||||||
|
name: 在終端機輸入 `echo next`
|
||||||
|
requirements:
|
||||||
|
- echo-hello
|
||||||
|
condition:
|
||||||
|
command: ^echo next$
|
||||||
|
response:
|
||||||
|
content: |
|
||||||
|
`Ctrl + C` 在終端機中是一個很重要的快捷鍵,它可以用來中斷正在執行的指令,並產生一個新的命令提示字串,讓你可以繼續輸入指令。
|
||||||
|
如果指令執行很久沒有反應,或你因為其他原因想要中斷指令,你可以按下 `Ctrl + C` 來中斷指令。
|
||||||
|
又或者你輸入了錯誤的指令,卻懶得慢慢刪除,你也可以按下 `Ctrl + C` 來產生新的命令提示字串重新輸入指令。
|
||||||
|
如果你已經理解了剛才的說明,請輸入 `echo finish` 來結束這一關。
|
||||||
|
hint: |
|
||||||
|
按下 `Ctrl + C` 能夠中斷正在執行的指令
|
||||||
|
按下 `Ctrl + C` 能夠產生新的命令提示字串
|
||||||
|
輸入 `echo finish` 來結束這一關
|
||||||
|
|
||||||
|
- id: END
|
||||||
|
name: 結束關卡
|
||||||
|
requirements:
|
||||||
|
- echo-next
|
||||||
|
condition:
|
||||||
|
command: ^echo finish$
|
||||||
|
response:
|
||||||
|
content: |
|
||||||
|
恭喜你,你已經完成了這個關卡!
|
||||||
@@ -40,7 +40,7 @@ export async function createContainer(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (!config.isProduction && config.docker.mountQuest && imageId !== 'base') {
|
if (!config.isProduction && config.docker.mountQuest && imageId !== 'base') {
|
||||||
logger.info('Mounting quest folder', questId)
|
logger.info('Mounting quest directory', questId)
|
||||||
binds = [
|
binds = [
|
||||||
`${config.docker.hostProjectRoot}/quests/${questId}/home:/home/commander`,
|
`${config.docker.hostProjectRoot}/quests/${questId}/home:/home/commander`,
|
||||||
`${config.docker.hostProjectRoot}/packages/container:/usr/local/lib/container`,
|
`${config.docker.hostProjectRoot}/packages/container:/usr/local/lib/container`,
|
||||||
|
|||||||
Reference in new issue
Block a user