Skip to content

Commit 966744a

Browse files
authored
chore: upgrade to Docusaurus 2 beta (#19)
- Upgrade to Docusaurus v2 beta - Use `classic` Docusaurus preset - List *features* on landing page - Add missing content to Part 1 to fix broken links - Use Yarn v1 as recommended by Docusaurus v2 - Fail if no build output is found Related to #17.
1 parent 41ce534 commit 966744a

40 files changed

+9562
-10144
lines changed

.github/workflows/website.yml

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ name: "RxJS Fundamentals website"
22

33
env:
44
NODE_OPTIONS: --max_old_space_size=6144
5-
NPM_CACHE_PATH: ~/.npm
65
node-version: 14.x
76
website-package-name: website
87

@@ -17,36 +16,33 @@ jobs:
1716
name: Build website
1817
runs-on: ubuntu-latest
1918

20-
env:
21-
working-directory: ./website
22-
2319
steps:
2420
- uses: actions/checkout@v2
2521
- name: Use Node.js ${{ env.node-version }}
2622
uses: actions/setup-node@v2
2723
with:
2824
node-version: ${{ env.node-version }}
2925

30-
- run: npm config set cache $NPM_CACHE_PATH
31-
working-directory: ${{ env.working-directory }}
32-
- name: Cache NPM cache directory
26+
- name: Variable-Yarn cache directory path
27+
id: yarn-cache-dir-path
28+
run: echo "::set-output name=dir::$(yarn cache dir)"
29+
- name: Cache Yarn cache directory
3330
uses: actions/cache@v2
3431
with:
35-
path: ${{ env.NPM_CACHE_PATH }}
36-
key: ${{ runner.os }}-npm-${{ hashFiles('./website/package-lock.json') }}
32+
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
33+
key: ${{ runner.os }}-node-${{ env.node-version }}-yarn-${{ hashFiles('yarn.lock') }}
3734
restore-keys: |
38-
${{ runner.os }}-npm-
39-
- run: npm ci
40-
working-directory: ${{ env.working-directory }}
35+
${{ runner.os }}-node-${{ env.node-version }}-yarn-
36+
${{ runner.os }}-node-${{ env.node-version }}-
37+
- run: yarn install
4138
- name: Build website
42-
run: npm run build
43-
working-directory: ${{ env.working-directory }}
39+
run: yarn build
4440
- name: "Upload website build artifact"
4541
uses: actions/upload-artifact@v2
4642
with:
4743
name: ${{ env.website-package-name }}
48-
path: ${{ env.working-directory }}/build/rxjs-fundamentals-course
49-
if-no-files-found: warn
44+
path: ./build/
45+
if-no-files-found: error
5046

5147
deploy:
5248
name: Deploy website to GitHub Pages

.gitignore

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
1-
website/node_modules/*
1+
# Dependencies
2+
/node_modules
3+
4+
# Production
5+
/build
6+
7+
# Generated files
8+
.docusaurus
9+
.cache-loader
10+
11+
# Misc
212
.DS_Store
3-
node_modules
4-
lib/core/metadata.js
5-
lib/core/MetadataBlog.js
6-
website/translated_docs
7-
website/build/
8-
website/yarn.lock
9-
website/i18n/*
13+
.env.local
14+
.env.development.local
15+
.env.test.local
16+
.env.production.local
17+
18+
npm-debug.log*
19+
yarn-debug.log*
20+
yarn-error.log*

babel.config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
3+
};

docs/part-1.md

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
id: part-1
3-
title: Part 1. RxJS: better async programming
3+
title: "Part 1. RxJS: Better async programming"
44
---
55

66
# RxJS: better async programming
@@ -135,9 +135,9 @@ The thing is, `Promises` are only one particular case of async operation - a sin
135135

136136
Now, let's take a look at some click events on a button. We start listening to the click events, but there is no guarantee that the callback will be invoked any time soon, right? Now, 5 minutes later the user finally clicks the button, and our callback works and handles the event, but it is not "resolved". The user can continue clicking on the button as many times as they want, and we would still have to handle that click. And after the user finally goes to another page, we have to stop listening to the clicks, because there is no button any more. We cannot represent a stream of events like clicks with a Promise, because a Promise works once and is destroyed afterwards. But `Observable` Streams of RxJS give us the ability to create streams, listen to their events, handle error cases, and also, handle the situation when the stream completes - like when the user went to another page in our example. So, in this regard, we can treat `Observables` as a far more powerful version of Promises, which deals with multiple events rather than one instance.
137137

138-
![](RackMultipart20210318-4-1e6dc56_html_19b10da71535a078.png)
138+
![Promise vs. RxJS](/img/part-1/promise-vs-rxjs.png)
139139

140-
(Picture by @thekiba_io)
140+
_Image by Andrew Grekov._
141141

142142
Now lets take a closer look.
143143

@@ -190,13 +190,33 @@ We will review some of these combinations and their use-cases in a later chapter
190190

191191
At first glance — Observables are just advanced Promises: Promises emits one value and complete (resolve), Observables emit 0, one or many values and complete as well (emit and complete are different actions). For HTTP service in AngularJS (where it used Promises) and Angular (where it uses Observables) provides only one value — so seems both frameworks work very similar in this case.
192192

193-
![](RackMultipart20210318-4-1e6dc56_html_fc59ddf18c80e00b.png)
193+
```ts
194+
// Observables in Angular 2+
195+
const source$ = this.http.get('https://example.com');
196+
source$.subscribe({
197+
next: data => handleData(data), // Success handler
198+
error: error => handleError(error), // Error handler
199+
complete: () => completeHandler(), // Complete handler
200+
});
201+
```
194202

195203
but if you application is doing something more then 'Hello world' — please pay attention to the differences.
196204

197205
### #1 Eager vs Lazy
198206

199-
Take a look at the example below: ![](RackMultipart20210318-4-1e6dc56_html_533e70b36f8382d3.png)
207+
Take a look at the example below:
208+
209+
```ts
210+
// Promise-wrapped HTTP request
211+
saveChanges(data) {
212+
return $http.post('https://example.com', data);
213+
}
214+
215+
// Observable-wrapped HTTP request
216+
saveChanges(data) {
217+
return this.http.post('https://example.com', data); // Does not send request!
218+
}
219+
```
200220

201221
When I call the `saveChanges` method — the first example with `Promise`-wrapped request will work as expected. But in seconds `Observable`-wrapped example nothing will happen because `Observables` are lazily-evaluated while `Promises` are eagerly-evaluated.
202222

@@ -212,13 +232,42 @@ To keep an eye on it — use _rxjs-no-ignored-observable_ rule from [rxjs-tslint
212232

213233
Again, start with an example when we do search on a backend on input text change
214234

215-
![](RackMultipart20210318-4-1e6dc56_html_518aba158767f496.png)
235+
```html
236+
<input ngKeyup="onKeyUp($event)>
237+
```
238+
239+
```ts
240+
// Promise-wrapped HTTP request
241+
saveChanges(event) {
242+
const text = event.target.value;
243+
$http.get('https://example.com?search=' + text)
244+
.then(searchResult => showSearchResult(searchResult));
245+
}
246+
```
216247
217248
There is a drawback here — you cannot reject results of the previous request if the user continues typing (`debounce` make this problem a bit less but doesn't eliminate it). And yet another issue — race conditions are possible (when a later request result will come back faster then an earlier one — so we get an incorrect response displayed).
218249
219250
`Observables` can avoid this concern quite elegantly with [_ **switchMap** _](https://rxjs-dev.firebaseapp.com/api/operators/switchMap) operator
220251
221-
![](RackMultipart20210318-4-1e6dc56_html_f31260205a770974.png)
252+
```html
253+
<input id="search">
254+
```
255+
256+
```ts
257+
// Observable-wrapped HTTP requet
258+
const inputElement = document.getElementById('search');
259+
const search$ = fromEvent(inputElement, 'keyup');
260+
261+
ngOnInit() {
262+
search$.pipe( // each time a new text value is emitted
263+
switchMap(event => { // switchMap cancels previous request and sends a new one
264+
const text = event.target.value;
265+
266+
return this.http.get('https://example.com?search=' + text);
267+
})
268+
).subscribe(newData => this.applyNewData(newData)); // Use new data
269+
}
270+
```
222271
223272
We will talk about `switchMap` and other higher-order-observable operators in the advanced RxJS course.
224273

docs/part-5.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
id: part-5
3-
title: Part 5. Everything is a stream: Push-based architecture
3+
title: "Part 5. Everything is a stream: Push-based architecture"
44
---
55

66
# Everything is a stream: Push-based architecture

docusaurus.config.js

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
const shadesOfPurpleTheme = require('prism-react-renderer/themes/shadesOfPurple');
2+
3+
const organizationName = 'this-is-learning';
4+
const projectName = 'rxjs-fundamentals-course';
5+
const title = 'RxJS Fundamentals';
6+
7+
// With JSDoc @type annotations, IDEs can provide config autocompletion
8+
/** @type {import('@docusaurus/types').DocusaurusConfig} */
9+
(module.exports = {
10+
baseUrl: `/${projectName}/`,
11+
favicon: 'img/favicon.ico',
12+
onBrokenLinks: 'throw',
13+
onBrokenMarkdownLinks: 'warn',
14+
presets: [
15+
[
16+
'@docusaurus/preset-classic',
17+
/** @type {import('@docusaurus/preset-classic').Options} */
18+
({
19+
blog: {
20+
editUrl: `https://github.com/${organizationName}/${projectName}/edit/main/blog`,
21+
showReadingTime: true,
22+
},
23+
docs: {
24+
editUrl: `https://github.com/${organizationName}/${projectName}/edit/main`,
25+
sidebarPath: require.resolve('./sidebars.js'),
26+
},
27+
theme: {
28+
customCss: require.resolve('./src/css/custom.css'),
29+
},
30+
}),
31+
],
32+
],
33+
projectName,
34+
tagline: 'An Open Learning course by This is Learning',
35+
themeConfig:
36+
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
37+
({
38+
footer: {
39+
copyright: ${new Date().getFullYear()} This is Learning. Licensed under CC BY-SA 4.0.`,
40+
links: [
41+
{
42+
title: 'Open Learning',
43+
items: [
44+
{
45+
label: 'RxJS Fundamentals',
46+
to: '/docs/part-1',
47+
},
48+
{
49+
label: 'This is Learning on DEV Community',
50+
href: 'https://dev.to/this-is-learning',
51+
},
52+
{
53+
label: 'This is Angular on DEV Community',
54+
href: 'https://dev.to/this-is-angular',
55+
},
56+
],
57+
},
58+
{
59+
title: 'Community',
60+
items: [
61+
{
62+
label: 'This is Learning Community on Discord',
63+
href: 'https://discord.gg/ygKzbrBtVn',
64+
},
65+
{
66+
label: 'This is Learning on GitHub',
67+
href: 'https://github.com/this-is-learning',
68+
},
69+
{
70+
label: 'This is Angular on GitHub',
71+
href: 'https://github.com/this-is-learning',
72+
},
73+
],
74+
},
75+
{
76+
title: 'Social',
77+
items: [
78+
{
79+
label: 'Star on GitHub',
80+
href: `https://github.com/${organizationName}/${projectName}`,
81+
},
82+
{
83+
label: 'This is Learning on Twitter',
84+
href: 'https://twitter.com/Thisis_Learning',
85+
},
86+
{
87+
label: 'This is Angular on Twitter',
88+
href: 'https://twitter.com/Thisis_Angular',
89+
},
90+
],
91+
},
92+
],
93+
style: 'dark',
94+
},
95+
navbar: {
96+
items: [
97+
{
98+
docId: 'part-1',
99+
label: 'Course',
100+
position: 'left',
101+
type: 'doc',
102+
},
103+
{
104+
href: `https://github.com/${organizationName}/${projectName}`,
105+
label: 'GitHub',
106+
position: 'right',
107+
},
108+
],
109+
logo: {
110+
alt: title,
111+
src: 'img/logo.png',
112+
},
113+
title,
114+
},
115+
prism: {
116+
darkTheme: shadesOfPurpleTheme,
117+
defaultLanguage: 'typescript',
118+
theme: shadesOfPurpleTheme,
119+
},
120+
}),
121+
title,
122+
url: `https://${organizationName}.github.io`,
123+
});

package.json

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"name": "rxjs-fundamentals-course",
3+
"version": "0.0.0",
4+
"private": true,
5+
"scripts": {
6+
"docusaurus": "docusaurus",
7+
"start": "docusaurus start",
8+
"build": "docusaurus build",
9+
"swizzle": "docusaurus swizzle",
10+
"deploy": "docusaurus deploy",
11+
"clear": "docusaurus clear",
12+
"serve": "docusaurus serve",
13+
"write-translations": "docusaurus write-translations",
14+
"write-heading-ids": "docusaurus write-heading-ids"
15+
},
16+
"dependencies": {
17+
"@docusaurus/core": "2.0.0-beta.6",
18+
"@docusaurus/preset-classic": "2.0.0-beta.6",
19+
"@mdx-js/react": "^1.6.21",
20+
"@svgr/webpack": "^5.5.0",
21+
"clsx": "^1.1.1",
22+
"file-loader": "^6.2.0",
23+
"prism-react-renderer": "^1.2.1",
24+
"react": "^17.0.1",
25+
"react-dom": "^17.0.1",
26+
"url-loader": "^4.1.1"
27+
},
28+
"browserslist": {
29+
"production": [
30+
">0.5%",
31+
"not dead",
32+
"not op_mini all"
33+
],
34+
"development": [
35+
"last 1 chrome version",
36+
"last 1 firefox version",
37+
"last 1 safari version"
38+
]
39+
}
40+
}

sidebars.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Creating a sidebar enables you to:
3+
- create an ordered group of docs
4+
- render a sidebar for each doc of that group
5+
- provide next/previous navigation
6+
7+
The sidebars can be generated from the filesystem, or explicitly defined here.
8+
9+
Create as many sidebars as you want.
10+
*/
11+
12+
module.exports = {
13+
// By default, Docusaurus generates a sidebar from the docs folder structure
14+
tutorialSidebar: [{type: 'autogenerated', dirName: '.'}],
15+
16+
// But you can create a sidebar manually
17+
/*
18+
tutorialSidebar: [
19+
{
20+
type: 'category',
21+
label: 'Tutorial',
22+
items: ['hello'],
23+
},
24+
],
25+
*/
26+
};

0 commit comments

Comments
 (0)