Skip to content

Commit

Permalink
Merge pull request #241 from valerymelou/feature/prerender-articles
Browse files Browse the repository at this point in the history
feat: pre-render articles
  • Loading branch information
valerymelou authored Jul 20, 2024
2 parents 244da1b + fc44acf commit c41cd3e
Show file tree
Hide file tree
Showing 40 changed files with 917 additions and 304 deletions.
5 changes: 5 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
"ignorePatterns": ["**/*"],
"plugins": ["@nx"],
"overrides": [
{
"files": "*.json",
"parser": "jsonc-eslint-parser",
"rules": {}
},
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ jobs:
cache: 'yarn'

- run: yarn install
# Generate the routes for prerendering. This is the only way I found to pass
# environment variables to the executor.
- run: VM_CONTENTFUL_SPACE=${{ env.VM_CONTENTFUL_SPACE }} VM_CONTENTFUL_ACCESS_TOKEN=${{ env.VM_CONTENTFUL_ACCESS_TOKEN }} VM_CONTENTFUL_ENVIRONMENT=${{ env.VM_CONTENTFUL_ENVIRONMENT }} yarn nx run website:build-routes
- run: npx nx run website:prerender

- name: Archive artifacts
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ jobs:
node-version: 20
cache: 'yarn'

- name: Restore NX Cache
id: nx-cache-restore
uses: actions/cache/restore@v4
with:
path: |
.nx
key: ${{ runner.os }}-nx

- run: yarn install
- run: npx nx affected --target=lint --parallel=3
- run: npx nx run-many --target=test --parallel=3 --code-coverage
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ Thumbs.db
.angular
.env
.firebaserc
routes.txt
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"eslint.validate": ["json"]
}
11 changes: 10 additions & 1 deletion apps/website/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@
"prerender": {
"executor": "@angular-devkit/build-angular:prerender",
"options": {
"routes": ["/"]
"routes": ["/"],
"routesFile": "apps/website/src/routes.txt"
},
"configurations": {
"development": {
Expand All @@ -136,6 +137,14 @@
}
},
"defaultConfiguration": "production"
},
"build-routes": {
"executor": "@valerymelou/blog-tasks:routes",
"options": {
"contentType": "article",
"outputPath": "apps/website/src/routes.txt",
"limit": "10"
}
}
}
}
3 changes: 3 additions & 0 deletions apps/website/public/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Allow URLs (see https://www.robotstxt.org/robotstxt.html)
User-agent: *
Disallow:
5 changes: 4 additions & 1 deletion apps/website/src/app/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ export const appConfig: ApplicationConfig = {
matches: true,
}),
location: {
origin: 'https://valerymelou.com',
origin:
process.env['VM_CONTENTFUL_ENVIRONMENT'] === 'master'
? 'https://valerymelou.com'
: 'https://staging.valerymelou.com/',
href: '',
},
};
Expand Down
10 changes: 10 additions & 0 deletions apps/website/src/app/app.routes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { Route } from '@angular/router';
import {
articleResolver,
articlesResolver,
} from '@valerymelou/blog/data-access';
import { BaseLayoutComponent } from '@valerymelou/shared/layout';
import { themeResolver } from '@valerymelou/shared/theming';

Expand Down Expand Up @@ -35,6 +39,9 @@ export const appRoutes: Route[] = [
loadComponent: () =>
import('@valerymelou/blog/home').then((c) => c.BlogHomeComponent),
data: { animation: 'BlogHomePage' },
resolve: {
articles: articlesResolver,
},
},
{
path: 'blog/:slug',
Expand All @@ -43,6 +50,9 @@ export const appRoutes: Route[] = [
(c) => c.BlogArticleComponent,
),
data: { animation: 'BlogHomePage' },
resolve: {
article: articleResolver,
},
},
],
},
Expand Down
2 changes: 2 additions & 0 deletions libs/blog/data-access/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export * from './lib/article';
export * from './lib/article.resolver';
export * from './lib/articles.resolver';
export * from './lib/article.service';
export * from './lib/results';
37 changes: 37 additions & 0 deletions libs/blog/data-access/src/lib/article.resolver.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { TestBed } from '@angular/core/testing';
import {
ActivatedRouteSnapshot,
ResolveFn,
RouterStateSnapshot,
} from '@angular/router';

import { of } from 'rxjs';

import { Article } from './article';
import { articleResolver } from './article.resolver';
import { ArticleService } from './article.service';

describe('articleResolver', () => {
const executeResolver: ResolveFn<Article> = (...resolverParameters) =>
TestBed.runInInjectionContext(() => articleResolver(...resolverParameters));

beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{
provide: ArticleService,
useValue: { getOne: () => of(new Article()) },
},
],
});
});

it('should get the article', () => {
const route: unknown = { params: { slug: '2024-07-20-test-article' } };
const articleService = TestBed.inject(ArticleService);
const getOneSpy = jest.spyOn(articleService, 'getOne');

executeResolver(route as ActivatedRouteSnapshot, {} as RouterStateSnapshot);
expect(getOneSpy).toHaveBeenCalledWith('test-article');
});
});
12 changes: 12 additions & 0 deletions libs/blog/data-access/src/lib/article.resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';

import { Article } from './article';
import { ArticleService } from './article.service';

export const articleResolver: ResolveFn<Article> = (route) => {
const articleService = inject(ArticleService);
const slug = route.params['slug'].split('-').slice(3).join('-');

return articleService.getOne(slug);
};
13 changes: 13 additions & 0 deletions libs/blog/data-access/src/lib/article.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,4 +373,17 @@ describe('ArticleService', () => {
expect(articles.items.length === 1);
});
});

it('should get one article entry', () => {
service
.getOne(
'the-django-administration-site-one-of-the-reasons-why-i-love-django',
)
.subscribe((article: Article) => {
expect(
article.title ===
'The Django administration site: One of the reasons why I love Django',
);
});
});
});
42 changes: 42 additions & 0 deletions libs/blog/data-access/src/lib/articles.resolver.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { TestBed } from '@angular/core/testing';
import {
ActivatedRouteSnapshot,
ResolveFn,
RouterStateSnapshot,
} from '@angular/router';

import { articlesResolver } from './articles.resolver';
import { Results } from './results';
import { Article } from './article';
import { ArticleService } from './article.service';
import { of } from 'rxjs';

describe('articlesResolver', () => {
const executeResolver: ResolveFn<Results<Article>> = (
...resolverParameters
) =>
TestBed.runInInjectionContext(() =>
articlesResolver(...resolverParameters),
);

beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{
provide: ArticleService,
useValue: {
get: () => of({ items: [new Article(), new Article()] }),
},
},
],
});
});

it('should get the articles', () => {
const articleService = TestBed.inject(ArticleService);
const getSpy = jest.spyOn(articleService, 'get');

executeResolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot);
expect(getSpy).toHaveBeenCalled();
});
});
12 changes: 12 additions & 0 deletions libs/blog/data-access/src/lib/articles.resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';

import { Article } from './article';
import { Results } from './results';
import { ArticleService } from './article.service';

export const articlesResolver: ResolveFn<Results<Article>> = () => {
const articleService = inject(ArticleService);

return articleService.get({});
};
Loading

0 comments on commit c41cd3e

Please sign in to comment.