Skip to content

Commit

Permalink
chore: Include basic parser and service
Browse files Browse the repository at this point in the history
  • Loading branch information
spuxx1701 committed May 23, 2024
1 parent 1698e63 commit 73bf309
Show file tree
Hide file tree
Showing 27 changed files with 599 additions and 46 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ An article's front matter may include the `parser` attribute. If that is the cas

```yaml
---
parser: character-origin
extension: character-origin
---
# ...
```
Expand Down
2 changes: 1 addition & 1 deletion books/basic-rules/appendix-character-origin/ceres/de.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
parser: character-origin
extension: character-origin
---

## Ceres und der Asteroidengürtel
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
parser: character-origin
extension: character-origin
---

## Silbernes Reich — Minenkolonien
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
parser: character-origin
extension: character-origin
---

## Silbernes Reich — Triton
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
parser: character-origin
extension: character-origin
---

## Erde — Abseits der Städte
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
parser: character-origin
extension: character-origin
---

## Erde — Großstädte und Raumstationen
Expand Down
2 changes: 1 addition & 1 deletion books/basic-rules/appendix-character-origin/europa/de.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
parser: character-origin
extension: character-origin
---

## Europa
Expand Down
2 changes: 1 addition & 1 deletion books/basic-rules/appendix-character-origin/ganymede/de.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
parser: character-origin
extension: character-origin
---

## Ganymed
Expand Down
2 changes: 1 addition & 1 deletion books/basic-rules/appendix-character-origin/mars/de.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
parser: character-origin
extension: character-origin
---

## Mars
Expand Down
2 changes: 1 addition & 1 deletion books/basic-rules/appendix-character-origin/titan/de.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
parser: character-origin
extension: character-origin
---

## Titan
Expand Down
2 changes: 1 addition & 1 deletion books/basic-rules/appendix-character-origin/venus/de.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
parser: character-origin
extension: character-origin
---

## Venus
Expand Down
2 changes: 2 additions & 0 deletions lib/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './wiki.config';
export * from './types';
19 changes: 19 additions & 0 deletions lib/config/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const WikiLocale = {
de: 'de',
} as const;
export type WikiLocale = (typeof WikiLocale)[keyof typeof WikiLocale];

export interface WikiConfig {
/**
* The base URL of the wiki.
*/
BASE_URL: string;
/**
* The locale of the wiki.
*/
LOCALE: WikiLocale;
/**
* The headers to use for outgoing HTTP requests.
*/
HTTP_HEADERS?: Record<string, string>;
}
7 changes: 7 additions & 0 deletions lib/config/wiki.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { WikiLocale, type WikiConfig } from './types';

export const wikiConfig: WikiConfig = {
BASE_URL: 'https://raw.githubusercontent.com/satellite-games/new-horizons-wiki/main/books',
LOCALE: WikiLocale.de,
HTTP_HEADERS: {},
};
6 changes: 5 additions & 1 deletion lib/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@
// Make sure to re-export all data and objects through this file. Otherwise
// they will not be included into the bundle.
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
export * from './parsers';
// Config
export * from './config';

// Services
export * from './services/wiki';
38 changes: 16 additions & 22 deletions lib/parsers/index.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
import { characterOriginParser } from './character-origin.parser';
// import { characterOriginParser } from '../services/parser/parsers/character-origin.parser';

/**
* This is the collection of parsers that can be used to enrich articles provided by
* `newhorizons-wiki` with in-game information. Each parser is a function that takes
* takes a Markdown string as input and returns a Markdown string with the parsed information.
* For more information on the wiki, see: https://github.com/satellite-games/newhorizons-wiki
*/
export const wikiArticleParsers: Record<string, WikiArticleParserFunction> = {
'character-origin': characterOriginParser,
};
// /**
// * This is the collection of parsers that can be used to enrich articles provided by
// * `newhorizons-wiki` with in-game information. Each parser is a function that takes
// * takes a Markdown string as input and returns a Markdown string with the parsed information.
// * For more information on the wiki, see: https://github.com/satellite-games/newhorizons-wiki
// */

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type WikiArticleParserFunction = (article: string, ...args: any[]) => Promise<string>;

/**
* Parses the given wiki article and returns the parsed article.
* @param article The article to parse.
*/
export function parseWikiArticle(article: string, parser: keyof typeof wikiArticleParsers) {
if (!wikiArticleParsers[parser]) return;
const parsedArticle = wikiArticleParsers[parser](article);
return parsedArticle;
}
// /**
// * Parses the given wiki article and returns the parsed article.
// * @param article The article to parse.
// */
// export function parseWikiArticle(article: string, parser: keyof typeof wikiArticleParsers) {
// if (!wikiArticleParsers[parser]) return;
// const parsedArticle = wikiArticleParsers[parser](article);
// return parsedArticle;
// }
2 changes: 2 additions & 0 deletions lib/services/parser/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './parser.service';
export * from './parser.registry';
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { CharacterOriginName } from '@newhorizons/core';

Check failure on line 1 in lib/services/parser/parser-extensions/character-origin.pex.test.ts

View workflow job for this annotation

GitHub Actions / Run code checks / run_checks (20)

Cannot find module '@newhorizons/core' or its corresponding type declarations.
import { characterOriginMocks } from '@newhorizons/core/mocks';

Check failure on line 2 in lib/services/parser/parser-extensions/character-origin.pex.test.ts

View workflow job for this annotation

GitHub Actions / Run code checks / run_checks (20)

Cannot find module '@newhorizons/core/mocks' or its corresponding type declarations.
import { beforeEach, expect, it, vi } from 'vitest';
import { characterOriginParser } from './character-origin.parser';
import type { CharacterOriginName } from '@newhorizons/core';
import { characterOriginParser } from './character-origin.pex';

beforeEach(() => {
vi.mock('@/main', () => {
Expand Down
File renamed without changes.
6 changes: 6 additions & 0 deletions lib/services/parser/parser.registry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { ParserFunction } from '@/types/private-types';
import { characterOriginParser } from './parser-extensions/character-origin.pex';

export const parserRegistry: Record<string, ParserFunction> = {
'character-origin': characterOriginParser,
};
55 changes: 55 additions & 0 deletions lib/services/parser/parser.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { ServiceMixin } from '@spuxx/browser-utils';
import { marked } from 'marked';

Check failure on line 2 in lib/services/parser/parser.service.ts

View workflow job for this annotation

GitHub Actions / Run code checks / run_checks (20)

Cannot find module 'marked' or its corresponding type declarations.
import { sanitize } from 'isomorphic-dompurify';

Check failure on line 3 in lib/services/parser/parser.service.ts

View workflow job for this annotation

GitHub Actions / Run code checks / run_checks (20)

Cannot find module 'isomorphic-dompurify' or its corresponding type declarations.
import yaml from 'js-yaml';

Check failure on line 4 in lib/services/parser/parser.service.ts

View workflow job for this annotation

GitHub Actions / Run code checks / run_checks (20)

Cannot find module 'js-yaml' or its corresponding type declarations.
import { WikiService } from '../wiki/wiki.service';

export class WikiParser extends ServiceMixin<WikiParser>() {
frontmatterPattern = /^---\s*([\s\S]*?)\s*---/;

/**
* Parses the Markdown article and to HTML.
* @param markdown The Markdown to parse.
* @returns The parsed HTML.
*/
public static async parse(article: string): Promise<string> {
// const frontmatter = this.extractFrontmatter(article);
let parsedArticle = this.removeFrontmatter(article);
parsedArticle = this.doSimpleReplacements(parsedArticle);
const html = await marked.parse(parsedArticle);
const sanitizedHtml = sanitize(html);
return sanitizedHtml;
}

/**
* Extracts the frontmatter from a markdown string.
* @param markdown The markdown string.
* @returns The frontmatter as a JavaScript object.
*/
public static extractFrontmatter(markdown: string): Record<string, unknown> | undefined {
const match = markdown.match(new RegExp(this.instance.frontmatterPattern));
if (!match) return;
const frontmatter = yaml.load(match[1]);
return frontmatter as Record<string, unknown>;
}

/**
* Removes the frontmatter from a markdown string.
* @param markdown The markdown string.
* @returns The markdown string without the frontmatter.
*/
private static removeFrontmatter(markdown: string): string {
return markdown.replace(new RegExp(this.instance.frontmatterPattern), '');
}

private static doSimpleReplacements(article: string) {
let parsedArticle = article;
// Replace partial image urls will full urls
const imageSrcPattern = /<img.*src=['"](\/books)/gi;
const imageSrcMatches = parsedArticle.matchAll(imageSrcPattern);
for (const match of imageSrcMatches) {
parsedArticle = parsedArticle.replace(match[1], WikiService.config.BASE_URL);
}
return parsedArticle;
}
}
2 changes: 2 additions & 0 deletions lib/services/wiki/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './wiki.service';
export * from './types';
20 changes: 20 additions & 0 deletions lib/services/wiki/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export interface WikiBookHeader {
id: string;
title: Record<string, string>;
icon: string;
default: {
chapter: string;
article: string;
};
}

export interface WikiBookToc extends WikiBookHeader {
chapters?: {
id: string;
title: Record<string, string>;
articles?: {
id: string;
title: Record<string, string>;
}[];
}[];
}
77 changes: 77 additions & 0 deletions lib/services/wiki/wiki.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { wikiConfig, type WikiConfig } from '@/config';
import { ServiceMixin } from '@spuxx/browser-utils';
import yaml from 'js-yaml';

Check failure on line 3 in lib/services/wiki/wiki.service.ts

View workflow job for this annotation

GitHub Actions / Run code checks / run_checks (20)

Cannot find module 'js-yaml' or its corresponding type declarations.
import { WikiParser } from '../parser';
import type { WikiBookHeader, WikiBookToc } from './types';

/**
* Provides functionalities to interact with the wiki.
*/
export class WikiService extends ServiceMixin<WikiService>() {
private _config: WikiConfig = wikiConfig;

static get config() {
return this.instance._config;
}

/**
* Updates the configuration of the wiki. Will merge the given
* configuration wih the packages default configuration.
* @param config The new configuration.
*/
static setConfig(config: Partial<WikiConfig>) {
this.instance._config = { ...wikiConfig, ...config };
}

/**
* Attempts to fetch an article and return it.
* @param book The book id.
* @param chapter The chapter id.
* @param article The article id.
* @returns The article.
*/
public static async fetchArticle(book: string, chapter: string, article: string) {
const url = `${this.config.BASE_URL}/${book}/${chapter}/${article}/${this.config.LOCALE}.md`;
try {
const response = await fetch(url, { headers: this.config.HTTP_HEADERS });
const text = await response.text();
const html = await WikiParser.parse(text);
return html;
} catch (error) {
throw new Error(`Unable to retrieve article from '${url}'.`);
}
}

/**
* Attempts to fetch the list of all available books.
* @returns The list of books.
*/
public static async fetchBooks(): Promise<WikiBookHeader[]> {
const url = `${this.config.BASE_URL}/books.yaml`;
try {
const response = await fetch(url);
const text = await response.text();
const books = yaml.load(text) as WikiBookHeader[];
return books;
} catch (error) {
throw new Error(`Unable to retrieve list of books.`);
}
}

/**
* Attempts to fetch the table of contents for the Stellarpedia.
* @param book The book id.
* @returns The table of contents.
*/
public static async fetchToc(book: string): Promise<WikiBookToc> {
const url = `${this.config.BASE_URL}/${book}/toc.yaml`;
try {
const response = await fetch(url);
const text = await response.text();
const toc = yaml.load(text) as WikiBookToc;
return toc;
} catch (error) {
throw new Error(`Unable to retrieve table of contents for book '${book}'.`);
}
}
}
2 changes: 2 additions & 0 deletions lib/types/private-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type ParserFunction = (article: string, ...args: any[]) => Promise<string>;
10 changes: 8 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,17 @@
"format": "prettier --write ."
},
"peerDependencies": {
"@newhorizons/core": "link:../newhorizons-core"
"@newhorizons/core": "link:../newhorizons-core",
"@spuxx/browser-utils": "^0.2.0"
},
"dependencies": {
"isomorphic-dompurify": "^2.11.0",
"js-yaml": "^4.1.0",
"marked": "^12.0.2"
},
"devDependencies": {
"@modyfi/vite-plugin-yaml": "^1.1.0",
"@spuxx/browser-utils": "^0.2.0",
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.12.10",
"@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^7.0.0",
Expand Down
Loading

0 comments on commit 73bf309

Please sign in to comment.