@learnpack/learnpack 5.0.275 → 5.0.276
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +409 -409
- package/lib/commands/audit.js +15 -15
- package/lib/commands/breakToken.js +19 -19
- package/lib/commands/clean.js +3 -3
- package/lib/commands/init.js +41 -41
- package/lib/commands/logout.js +3 -3
- package/lib/commands/publish.js +5 -10
- package/lib/commands/serve.js +3 -2
- package/lib/creatorDist/assets/index-BfLyIQVh.js +10343 -10224
- package/lib/managers/config/index.js +77 -77
- package/lib/utils/api.d.ts +1 -1
- package/lib/utils/api.js +12 -9
- package/lib/utils/creatorUtilities.js +14 -14
- package/package.json +1 -1
- package/src/commands/audit.ts +487 -487
- package/src/commands/breakToken.ts +67 -67
- package/src/commands/clean.ts +30 -30
- package/src/commands/init.ts +650 -650
- package/src/commands/logout.ts +38 -38
- package/src/commands/publish.ts +20 -25
- package/src/commands/serve.ts +8 -3
- package/src/commands/start.ts +333 -333
- package/src/commands/translate.ts +123 -123
- package/src/creator/README.md +54 -54
- package/src/creator/eslint.config.js +28 -28
- package/src/creator/src/components/syllabus/ContentIndex.tsx +312 -312
- package/src/creator/src/i18n.ts +28 -28
- package/src/creator/src/index.css +217 -217
- package/src/creator/src/locales/en.json +126 -126
- package/src/creator/src/locales/es.json +126 -126
- package/src/creator/src/utils/configTypes.ts +122 -122
- package/src/creator/src/utils/constants.ts +13 -13
- package/src/creator/src/utils/creatorUtils.ts +46 -46
- package/src/creator/src/utils/eventBus.ts +2 -2
- package/src/creator/src/utils/lib.ts +468 -468
- package/src/creator/src/utils/socket.ts +61 -61
- package/src/creator/src/utils/store.ts +222 -222
- package/src/creator/src/vite-env.d.ts +1 -1
- package/src/creator/vite.config.ts +13 -13
- package/src/creatorDist/assets/index-BfLyIQVh.js +10343 -10224
- package/src/managers/config/defaults.ts +49 -49
- package/src/managers/config/exercise.ts +364 -364
- package/src/managers/config/index.ts +775 -775
- package/src/managers/file.ts +236 -236
- package/src/managers/server/routes.ts +554 -554
- package/src/managers/session.ts +182 -182
- package/src/managers/telemetry.ts +188 -188
- package/src/models/action.ts +13 -13
- package/src/models/config-manager.ts +28 -28
- package/src/models/config.ts +106 -106
- package/src/models/creator.ts +47 -47
- package/src/models/exercise-obj.ts +30 -30
- package/src/models/session.ts +39 -39
- package/src/models/socket.ts +61 -61
- package/src/models/status.ts +16 -16
- package/src/ui/_app/app.css +1 -1
- package/src/ui/_app/app.js +366 -363
- package/src/ui/app.tar.gz +0 -0
- package/src/utils/BaseCommand.ts +56 -56
- package/src/utils/api.ts +53 -39
- package/src/utils/audit.ts +392 -392
- package/src/utils/checkNotInstalled.ts +267 -267
- package/src/utils/configBuilder.ts +82 -82
- package/src/utils/convertCreds.js +34 -34
- package/src/utils/creatorUtilities.ts +504 -504
- package/src/utils/incrementVersion.js +74 -74
- package/src/utils/misc.ts +58 -58
- package/src/utils/rigoActions.ts +500 -500
- package/src/utils/sidebarGenerator.ts +195 -195
- package/src/utils/templates/isolated/exercises/01-hello-world/README.es.md +26 -26
- package/src/utils/templates/isolated/exercises/01-hello-world/README.md +26 -26
@@ -1,195 +1,195 @@
|
|
1
|
-
import path = require("path")
|
2
|
-
|
3
|
-
import Console from "./console"
|
4
|
-
import fs = require("fs")
|
5
|
-
import { IExercise, IExerciseData } from "../models/exercise-obj"
|
6
|
-
import { IConfigObj } from "../models/config"
|
7
|
-
import SessionManager from "../managers/session"
|
8
|
-
import { fillSidebarJSON } from "./rigoActions"
|
9
|
-
|
10
|
-
type TTitleTranslations = {
|
11
|
-
[key: string]: string
|
12
|
-
}
|
13
|
-
|
14
|
-
export type TSidebar = {
|
15
|
-
[key: string]: TTitleTranslations
|
16
|
-
}
|
17
|
-
|
18
|
-
export const generateSidebar = (exercises: IExercise[], learnPath: string) => {
|
19
|
-
const sidebarPath = path.join(learnPath, "sidebar.json")
|
20
|
-
let sidebar: TSidebar = {}
|
21
|
-
if (fs.existsSync(sidebarPath)) {
|
22
|
-
sidebar = JSON.parse(fs.readFileSync(sidebarPath, "utf8"))
|
23
|
-
}
|
24
|
-
|
25
|
-
for (const exercise of exercises) {
|
26
|
-
sidebar[exercise.slug] = {
|
27
|
-
...sidebar[exercise.slug],
|
28
|
-
us: exercise.title,
|
29
|
-
}
|
30
|
-
}
|
31
|
-
|
32
|
-
fs.writeFileSync(sidebarPath, JSON.stringify(sidebar, null, 2))
|
33
|
-
|
34
|
-
return sidebar
|
35
|
-
}
|
36
|
-
|
37
|
-
export const addExerciseToSidebar = (
|
38
|
-
exerciseSlug: string,
|
39
|
-
targetLanguage: string,
|
40
|
-
translatedSlug: string,
|
41
|
-
learnPath: string
|
42
|
-
) => {
|
43
|
-
const sidebarPath = path.join(learnPath, "sidebar.json")
|
44
|
-
let sidebar: TSidebar = {}
|
45
|
-
if (fs.existsSync(sidebarPath)) {
|
46
|
-
sidebar = JSON.parse(fs.readFileSync(sidebarPath, "utf8"))
|
47
|
-
}
|
48
|
-
|
49
|
-
sidebar[exerciseSlug] = {
|
50
|
-
...sidebar[exerciseSlug],
|
51
|
-
[targetLanguage]: translatedSlug,
|
52
|
-
}
|
53
|
-
|
54
|
-
fs.writeFileSync(sidebarPath, JSON.stringify(sidebar, null, 2))
|
55
|
-
|
56
|
-
return sidebar
|
57
|
-
}
|
58
|
-
|
59
|
-
export const checkAndFixSidebar = async (
|
60
|
-
configObj: IConfigObj,
|
61
|
-
autoFix = false
|
62
|
-
): Promise<boolean> => {
|
63
|
-
if (
|
64
|
-
configObj.config &&
|
65
|
-
fs.existsSync(configObj.config.dirPath + "/sidebar.json")
|
66
|
-
) {
|
67
|
-
let hasErrors = false
|
68
|
-
const sidebar = fs.readFileSync(
|
69
|
-
configObj.config.dirPath + "/sidebar.json",
|
70
|
-
"utf8"
|
71
|
-
)
|
72
|
-
// parse the sidebar.json file
|
73
|
-
const sidebarJson = JSON.parse(sidebar)
|
74
|
-
|
75
|
-
const exerciseTranslations: Set<string> = new Set()
|
76
|
-
configObj.exercises?.map(e =>
|
77
|
-
// eslint-disable-next-line
|
78
|
-
Object.keys((e.translations || {}) as any).forEach((t) =>
|
79
|
-
exerciseTranslations.add(t)
|
80
|
-
)
|
81
|
-
)
|
82
|
-
// Validation
|
83
|
-
for (const [key, value] of Object.entries(sidebarJson) as [
|
84
|
-
string,
|
85
|
-
TTitleTranslations
|
86
|
-
][]) {
|
87
|
-
for (const lang of exerciseTranslations) {
|
88
|
-
if (!Object.prototype.hasOwnProperty.call(value, lang)) {
|
89
|
-
hasErrors = true
|
90
|
-
}
|
91
|
-
}
|
92
|
-
}
|
93
|
-
|
94
|
-
if (hasErrors && autoFix) {
|
95
|
-
Console.warning("Filling sidebar.json file with missing translations")
|
96
|
-
const sessionPayload = await SessionManager.getPayload()
|
97
|
-
|
98
|
-
const rigoToken = sessionPayload.rigobot.key
|
99
|
-
|
100
|
-
if (!rigoToken) {
|
101
|
-
Console.error("No Rigobot token found, please login first!")
|
102
|
-
return false
|
103
|
-
}
|
104
|
-
|
105
|
-
const response = await fillSidebarJSON(rigoToken, {
|
106
|
-
needed_translations: JSON.stringify(exerciseTranslations),
|
107
|
-
sidebar_json: JSON.stringify(sidebarJson),
|
108
|
-
})
|
109
|
-
|
110
|
-
const newSidebarJson = JSON.parse(response.parsed.new_sidebar_file)
|
111
|
-
fs.writeFileSync(
|
112
|
-
configObj.config.dirPath + "/sidebar.json",
|
113
|
-
JSON.stringify(newSidebarJson, null, 4)
|
114
|
-
)
|
115
|
-
Console.info("Sidebar.json was filled with missing translations")
|
116
|
-
return true
|
117
|
-
}
|
118
|
-
|
119
|
-
if (hasErrors && !autoFix) {
|
120
|
-
return false
|
121
|
-
}
|
122
|
-
|
123
|
-
return true
|
124
|
-
}
|
125
|
-
|
126
|
-
return false
|
127
|
-
}
|
128
|
-
|
129
|
-
export const checkAndFixSidebarPure = async (
|
130
|
-
sidebarJson: Record<string, Record<string, string>>,
|
131
|
-
exercises: any[],
|
132
|
-
rigoToken: string,
|
133
|
-
autoFix = true
|
134
|
-
): Promise<{
|
135
|
-
valid: boolean
|
136
|
-
fixedSidebar?: Record<string, Record<string, string>>
|
137
|
-
}> => {
|
138
|
-
const exerciseTranslations: Set<string> = new Set()
|
139
|
-
for (const e of exercises) {
|
140
|
-
for (const lang of Object.keys(e.translations || {})) {
|
141
|
-
exerciseTranslations.add(lang)
|
142
|
-
}
|
143
|
-
}
|
144
|
-
|
145
|
-
let hasErrors = false
|
146
|
-
const fixedSidebar: Record<string, Record<string, string>> = {
|
147
|
-
...sidebarJson,
|
148
|
-
}
|
149
|
-
|
150
|
-
for (const exercise of exercises) {
|
151
|
-
const slug = exercise.slug
|
152
|
-
const existingEntry = fixedSidebar[slug]
|
153
|
-
|
154
|
-
if (!existingEntry) {
|
155
|
-
hasErrors = true
|
156
|
-
fixedSidebar[slug] = { us: exercise.slug }
|
157
|
-
continue
|
158
|
-
}
|
159
|
-
|
160
|
-
for (const lang of exerciseTranslations) {
|
161
|
-
if (!Object.prototype.hasOwnProperty.call(existingEntry, lang)) {
|
162
|
-
hasErrors = true
|
163
|
-
}
|
164
|
-
}
|
165
|
-
}
|
166
|
-
|
167
|
-
if (hasErrors && autoFix) {
|
168
|
-
if (!rigoToken) {
|
169
|
-
Console.error("No Rigobot token provided!")
|
170
|
-
return { valid: false }
|
171
|
-
}
|
172
|
-
|
173
|
-
Console.warning(
|
174
|
-
"Filling sidebar JSON with missing translations or exercises"
|
175
|
-
)
|
176
|
-
|
177
|
-
const response = await fillSidebarJSON(rigoToken, {
|
178
|
-
needed_translations: JSON.stringify([...exerciseTranslations]),
|
179
|
-
sidebar_json: JSON.stringify(fixedSidebar),
|
180
|
-
})
|
181
|
-
|
182
|
-
const newSidebarJson = JSON.parse(response.parsed.new_sidebar_file)
|
183
|
-
|
184
|
-
Console.info("Sidebar JSON was fixed")
|
185
|
-
return {
|
186
|
-
valid: true,
|
187
|
-
fixedSidebar: newSidebarJson,
|
188
|
-
}
|
189
|
-
}
|
190
|
-
|
191
|
-
return {
|
192
|
-
valid: !hasErrors,
|
193
|
-
fixedSidebar: hasErrors ? fixedSidebar : undefined,
|
194
|
-
}
|
195
|
-
}
|
1
|
+
import path = require("path")
|
2
|
+
|
3
|
+
import Console from "./console"
|
4
|
+
import fs = require("fs")
|
5
|
+
import { IExercise, IExerciseData } from "../models/exercise-obj"
|
6
|
+
import { IConfigObj } from "../models/config"
|
7
|
+
import SessionManager from "../managers/session"
|
8
|
+
import { fillSidebarJSON } from "./rigoActions"
|
9
|
+
|
10
|
+
type TTitleTranslations = {
|
11
|
+
[key: string]: string
|
12
|
+
}
|
13
|
+
|
14
|
+
export type TSidebar = {
|
15
|
+
[key: string]: TTitleTranslations
|
16
|
+
}
|
17
|
+
|
18
|
+
export const generateSidebar = (exercises: IExercise[], learnPath: string) => {
|
19
|
+
const sidebarPath = path.join(learnPath, "sidebar.json")
|
20
|
+
let sidebar: TSidebar = {}
|
21
|
+
if (fs.existsSync(sidebarPath)) {
|
22
|
+
sidebar = JSON.parse(fs.readFileSync(sidebarPath, "utf8"))
|
23
|
+
}
|
24
|
+
|
25
|
+
for (const exercise of exercises) {
|
26
|
+
sidebar[exercise.slug] = {
|
27
|
+
...sidebar[exercise.slug],
|
28
|
+
us: exercise.title,
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
fs.writeFileSync(sidebarPath, JSON.stringify(sidebar, null, 2))
|
33
|
+
|
34
|
+
return sidebar
|
35
|
+
}
|
36
|
+
|
37
|
+
export const addExerciseToSidebar = (
|
38
|
+
exerciseSlug: string,
|
39
|
+
targetLanguage: string,
|
40
|
+
translatedSlug: string,
|
41
|
+
learnPath: string
|
42
|
+
) => {
|
43
|
+
const sidebarPath = path.join(learnPath, "sidebar.json")
|
44
|
+
let sidebar: TSidebar = {}
|
45
|
+
if (fs.existsSync(sidebarPath)) {
|
46
|
+
sidebar = JSON.parse(fs.readFileSync(sidebarPath, "utf8"))
|
47
|
+
}
|
48
|
+
|
49
|
+
sidebar[exerciseSlug] = {
|
50
|
+
...sidebar[exerciseSlug],
|
51
|
+
[targetLanguage]: translatedSlug,
|
52
|
+
}
|
53
|
+
|
54
|
+
fs.writeFileSync(sidebarPath, JSON.stringify(sidebar, null, 2))
|
55
|
+
|
56
|
+
return sidebar
|
57
|
+
}
|
58
|
+
|
59
|
+
export const checkAndFixSidebar = async (
|
60
|
+
configObj: IConfigObj,
|
61
|
+
autoFix = false
|
62
|
+
): Promise<boolean> => {
|
63
|
+
if (
|
64
|
+
configObj.config &&
|
65
|
+
fs.existsSync(configObj.config.dirPath + "/sidebar.json")
|
66
|
+
) {
|
67
|
+
let hasErrors = false
|
68
|
+
const sidebar = fs.readFileSync(
|
69
|
+
configObj.config.dirPath + "/sidebar.json",
|
70
|
+
"utf8"
|
71
|
+
)
|
72
|
+
// parse the sidebar.json file
|
73
|
+
const sidebarJson = JSON.parse(sidebar)
|
74
|
+
|
75
|
+
const exerciseTranslations: Set<string> = new Set()
|
76
|
+
configObj.exercises?.map(e =>
|
77
|
+
// eslint-disable-next-line
|
78
|
+
Object.keys((e.translations || {}) as any).forEach((t) =>
|
79
|
+
exerciseTranslations.add(t)
|
80
|
+
)
|
81
|
+
)
|
82
|
+
// Validation
|
83
|
+
for (const [key, value] of Object.entries(sidebarJson) as [
|
84
|
+
string,
|
85
|
+
TTitleTranslations
|
86
|
+
][]) {
|
87
|
+
for (const lang of exerciseTranslations) {
|
88
|
+
if (!Object.prototype.hasOwnProperty.call(value, lang)) {
|
89
|
+
hasErrors = true
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
if (hasErrors && autoFix) {
|
95
|
+
Console.warning("Filling sidebar.json file with missing translations")
|
96
|
+
const sessionPayload = await SessionManager.getPayload()
|
97
|
+
|
98
|
+
const rigoToken = sessionPayload.rigobot.key
|
99
|
+
|
100
|
+
if (!rigoToken) {
|
101
|
+
Console.error("No Rigobot token found, please login first!")
|
102
|
+
return false
|
103
|
+
}
|
104
|
+
|
105
|
+
const response = await fillSidebarJSON(rigoToken, {
|
106
|
+
needed_translations: JSON.stringify(exerciseTranslations),
|
107
|
+
sidebar_json: JSON.stringify(sidebarJson),
|
108
|
+
})
|
109
|
+
|
110
|
+
const newSidebarJson = JSON.parse(response.parsed.new_sidebar_file)
|
111
|
+
fs.writeFileSync(
|
112
|
+
configObj.config.dirPath + "/sidebar.json",
|
113
|
+
JSON.stringify(newSidebarJson, null, 4)
|
114
|
+
)
|
115
|
+
Console.info("Sidebar.json was filled with missing translations")
|
116
|
+
return true
|
117
|
+
}
|
118
|
+
|
119
|
+
if (hasErrors && !autoFix) {
|
120
|
+
return false
|
121
|
+
}
|
122
|
+
|
123
|
+
return true
|
124
|
+
}
|
125
|
+
|
126
|
+
return false
|
127
|
+
}
|
128
|
+
|
129
|
+
export const checkAndFixSidebarPure = async (
|
130
|
+
sidebarJson: Record<string, Record<string, string>>,
|
131
|
+
exercises: any[],
|
132
|
+
rigoToken: string,
|
133
|
+
autoFix = true
|
134
|
+
): Promise<{
|
135
|
+
valid: boolean
|
136
|
+
fixedSidebar?: Record<string, Record<string, string>>
|
137
|
+
}> => {
|
138
|
+
const exerciseTranslations: Set<string> = new Set()
|
139
|
+
for (const e of exercises) {
|
140
|
+
for (const lang of Object.keys(e.translations || {})) {
|
141
|
+
exerciseTranslations.add(lang)
|
142
|
+
}
|
143
|
+
}
|
144
|
+
|
145
|
+
let hasErrors = false
|
146
|
+
const fixedSidebar: Record<string, Record<string, string>> = {
|
147
|
+
...sidebarJson,
|
148
|
+
}
|
149
|
+
|
150
|
+
for (const exercise of exercises) {
|
151
|
+
const slug = exercise.slug
|
152
|
+
const existingEntry = fixedSidebar[slug]
|
153
|
+
|
154
|
+
if (!existingEntry) {
|
155
|
+
hasErrors = true
|
156
|
+
fixedSidebar[slug] = { us: exercise.slug }
|
157
|
+
continue
|
158
|
+
}
|
159
|
+
|
160
|
+
for (const lang of exerciseTranslations) {
|
161
|
+
if (!Object.prototype.hasOwnProperty.call(existingEntry, lang)) {
|
162
|
+
hasErrors = true
|
163
|
+
}
|
164
|
+
}
|
165
|
+
}
|
166
|
+
|
167
|
+
if (hasErrors && autoFix) {
|
168
|
+
if (!rigoToken) {
|
169
|
+
Console.error("No Rigobot token provided!")
|
170
|
+
return { valid: false }
|
171
|
+
}
|
172
|
+
|
173
|
+
Console.warning(
|
174
|
+
"Filling sidebar JSON with missing translations or exercises"
|
175
|
+
)
|
176
|
+
|
177
|
+
const response = await fillSidebarJSON(rigoToken, {
|
178
|
+
needed_translations: JSON.stringify([...exerciseTranslations]),
|
179
|
+
sidebar_json: JSON.stringify(fixedSidebar),
|
180
|
+
})
|
181
|
+
|
182
|
+
const newSidebarJson = JSON.parse(response.parsed.new_sidebar_file)
|
183
|
+
|
184
|
+
Console.info("Sidebar JSON was fixed")
|
185
|
+
return {
|
186
|
+
valid: true,
|
187
|
+
fixedSidebar: newSidebarJson,
|
188
|
+
}
|
189
|
+
}
|
190
|
+
|
191
|
+
return {
|
192
|
+
valid: !hasErrors,
|
193
|
+
fixedSidebar: hasErrors ? fixedSidebar : undefined,
|
194
|
+
}
|
195
|
+
}
|
@@ -1,26 +1,26 @@
|
|
1
|
-
# `01` Primer Ejercicio
|
2
|
-
|
3
|
-
Hemos creado este primer ejercicio como ejemplo. Lo puedes ubicar en la carpeta `./01-hello-world`.
|
4
|
-
|
5
|
-
1. Cada ejercicio debe estar ubicado en carpetas separadas y debe tener un archivo README.md con las instrucciones del ejercicio escrito en markdown.
|
6
|
-
2. Puedes tener un archivo README el cual será como una página de un libro, sin archivos de código.
|
7
|
-
3. También puedes agregar un archivo `README.[lenguaje].md` para traducciones, por ejemplo `README.es.md` para español.
|
8
|
-
|
9
|
-
## Inserta videos
|
10
|
-
|
11
|
-
Si quieres incluir algún video introductorio para cada ejercicio, agrega la propiedad `intro` en el inicio del README.md para ese ejercicio en particular:
|
12
|
-
|
13
|
-
```markdown
|
14
|
-
---
|
15
|
-
intro: "https://www.youtube.com/watch?v=YkgkThdzX-8"
|
16
|
-
---
|
17
|
-
```
|
18
|
-
|
19
|
-
Tambien puedes agregar un video explicando la solución para cada ejercicio agregando la propiedad `tutorial` al inicio del markdown del README.md correspondiente:
|
20
|
-
|
21
|
-
```markdown
|
22
|
-
---
|
23
|
-
intro: "https://www.youtube.com/watch?v=YkgkThdzX-8"
|
24
|
-
tutorial: "https://www.youtube.com/watch?v=YkgkThdzX-8"
|
25
|
-
---
|
26
|
-
```
|
1
|
+
# `01` Primer Ejercicio
|
2
|
+
|
3
|
+
Hemos creado este primer ejercicio como ejemplo. Lo puedes ubicar en la carpeta `./01-hello-world`.
|
4
|
+
|
5
|
+
1. Cada ejercicio debe estar ubicado en carpetas separadas y debe tener un archivo README.md con las instrucciones del ejercicio escrito en markdown.
|
6
|
+
2. Puedes tener un archivo README el cual será como una página de un libro, sin archivos de código.
|
7
|
+
3. También puedes agregar un archivo `README.[lenguaje].md` para traducciones, por ejemplo `README.es.md` para español.
|
8
|
+
|
9
|
+
## Inserta videos
|
10
|
+
|
11
|
+
Si quieres incluir algún video introductorio para cada ejercicio, agrega la propiedad `intro` en el inicio del README.md para ese ejercicio en particular:
|
12
|
+
|
13
|
+
```markdown
|
14
|
+
---
|
15
|
+
intro: "https://www.youtube.com/watch?v=YkgkThdzX-8"
|
16
|
+
---
|
17
|
+
```
|
18
|
+
|
19
|
+
Tambien puedes agregar un video explicando la solución para cada ejercicio agregando la propiedad `tutorial` al inicio del markdown del README.md correspondiente:
|
20
|
+
|
21
|
+
```markdown
|
22
|
+
---
|
23
|
+
intro: "https://www.youtube.com/watch?v=YkgkThdzX-8"
|
24
|
+
tutorial: "https://www.youtube.com/watch?v=YkgkThdzX-8"
|
25
|
+
---
|
26
|
+
```
|
@@ -1,26 +1,26 @@
|
|
1
|
-
# `01` First Exercise
|
2
|
-
|
3
|
-
We created this first exercise as an example, you can find it located in the folder `./01-hello-world`.
|
4
|
-
|
5
|
-
1. Every exercise must be located on a separate folder and it must have a README.md file inside with the exercise instructions written in markdown.
|
6
|
-
2. You can have just a README file and it will be like a page in a book, no code files.
|
7
|
-
3. You can also add a `README.[lang].md` file for translations, for example: `README.es.md` for spanish.
|
8
|
-
|
9
|
-
## Video compatibility
|
10
|
-
|
11
|
-
If you want to include some video introduction for each exercise, add a `intro` property in the markdown frontmatter of the README.md for that particular exercise:
|
12
|
-
|
13
|
-
```markdown
|
14
|
-
---
|
15
|
-
intro: "https://www.youtube.com/watch?v=YkgkThdzX-8"
|
16
|
-
---
|
17
|
-
```
|
18
|
-
|
19
|
-
You can also add a video solution for each exercise by adding a `tutorial` property on the markdown frontmatter of it's README.md:
|
20
|
-
|
21
|
-
```markdown
|
22
|
-
---
|
23
|
-
intro: "https://www.youtube.com/watch?v=YkgkThdzX-8"
|
24
|
-
tutorial: "https://www.youtube.com/watch?v=YkgkThdzX-8"
|
25
|
-
---
|
26
|
-
```
|
1
|
+
# `01` First Exercise
|
2
|
+
|
3
|
+
We created this first exercise as an example, you can find it located in the folder `./01-hello-world`.
|
4
|
+
|
5
|
+
1. Every exercise must be located on a separate folder and it must have a README.md file inside with the exercise instructions written in markdown.
|
6
|
+
2. You can have just a README file and it will be like a page in a book, no code files.
|
7
|
+
3. You can also add a `README.[lang].md` file for translations, for example: `README.es.md` for spanish.
|
8
|
+
|
9
|
+
## Video compatibility
|
10
|
+
|
11
|
+
If you want to include some video introduction for each exercise, add a `intro` property in the markdown frontmatter of the README.md for that particular exercise:
|
12
|
+
|
13
|
+
```markdown
|
14
|
+
---
|
15
|
+
intro: "https://www.youtube.com/watch?v=YkgkThdzX-8"
|
16
|
+
---
|
17
|
+
```
|
18
|
+
|
19
|
+
You can also add a video solution for each exercise by adding a `tutorial` property on the markdown frontmatter of it's README.md:
|
20
|
+
|
21
|
+
```markdown
|
22
|
+
---
|
23
|
+
intro: "https://www.youtube.com/watch?v=YkgkThdzX-8"
|
24
|
+
tutorial: "https://www.youtube.com/watch?v=YkgkThdzX-8"
|
25
|
+
---
|
26
|
+
```
|