Skip to content

Commit

Permalink
feat(web app): finalize content-loading algorithm
Browse files Browse the repository at this point in the history
- finalized the Table of Contents generation algorithm
- converted all content to JSON (text is now stored directly in the Catechism structure, instead of a separate file)
- made PathID more meaningful
- added a `semanticPath` property to all content items
- URLs now use the more readable semantic path
- improved typings and code organization
  • Loading branch information
inkwell-studio committed Aug 26, 2023
1 parent 7cd2b59 commit 4b6d2dd
Show file tree
Hide file tree
Showing 106 changed files with 467,613 additions and 99,161 deletions.
13 changes: 13 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@
],
"attachSimplePort": 9229
},
{
"request": "launch",
"name": "Test (single test)",
"type": "node",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "deno",
"runtimeArgs": [
"test",
"--inspect-brk",
"web-utils/rendering.test.ts"
],
"attachSimplePort": 9229
},
{
"request": "launch",
"name": "Artifacts",
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ more details (optional)

Where `type` is one of:

- `feat`_for user-facing functionality additions and improvements_
- `fix`_for bug fixes_
- `refactor`_for code refactoring that does not substantially change user-facing functionality_
- `build`_for build changes_
- `style`_for merely cosmetic code changes_
- `docs`_for documentation changes_
- `feat`for user-facing functionality additions and improvements
- `fix`for bug fixes
- `refactor`for code refactoring that does not substantially change user-facing functionality
- `build`for build changes
- `style`for merely cosmetic code changes
- `docs`for documentation changes
56 changes: 11 additions & 45 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,17 @@
# Tasks

- [ ] implement proper content routing/rendering

## Rendering content

| content type selected | content type loaded and rendered |
| --------------------- | --------------------------------- |
| `Prologue` | Prologue |
| `Part` | `openingContent` and first child |
| `Section` | `openingContent` and first child |
| `Chapter` | `openingContent` and first child |
| `Article` | see _Rendering Article_ |
| `Article Paragraph` | see _Rendering Article Paragraph_ |
| all else | see _Rendering low-level content_ |

### Rendering `Article`

- If `mainContent` contains any `ArticleParagraph`s:
- render `openingContent` and `mainContent[0]`
- Else:
- render entire element

### Rendering `Article Paragraph`

- If it is the first child of its parent:
- render its parent according to its rule
- Else:
- render entire element

### Rendering low-level content

- Render the nearest ancestor of the following types according to its rule:
- `Prologue`
- `Part`
- `Section`
- `Chapter`
- `Article`
- `Article Paragraph`

---

- [ ] determine if the semantic-path-to-path-id map needs refactoring
- [ ] use `.json` files instead of "hand-built" `.ts` files (e.g. `catechism.ts`)

## Ungrouped

- [ ] determine feasibility of different data storage and retrieval mechanisms
- [ ] add e2e tests for URLs (headless, if possible)
- [ ] add multi-language support
- [ ] rename `catechism.ts` as `catechism-en.ts`
- [ ] rename variables appropriately
- [ ] rename other JSON artifacts
- [ ] determine routing logic
- [ ] add JSON validation for `catechism.json`
- [ ] render all content
- [ ] handle all TODO's in `content.tsx`
- [ ] re-do the rendering structures (reorganize components, etc.)
- [ ] opening content
- [ ] citation markers
Expand All @@ -54,8 +22,6 @@
- [ ] routing
- [ ] to in-page anchor tags

- [ ] styling

## Unprioritized

- UI:
Expand Down
18 changes: 10 additions & 8 deletions catechism-of-the-catholic-church.code-workspace
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
{
"folders": [
{
"path": "."
}
],
"settings": {
"deno.enable": true
}
"folders": [
{
"path": "."
}
],
"settings": {
"deno.enable": true,
"deno.lint": true,
"deno.unstable": false
}
}
2 changes: 1 addition & 1 deletion catechism/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# The Catechism of the Catholic Church

This contains the Catechism of the Catholic Church in an arrangement that decouples its structure, text, and visual
This contains the Catechism of the Catholic Church in an arrangement that decouples its content and visual
representation as much as possible. All information necessary for a visual rendering of the content is provided.
28 changes: 18 additions & 10 deletions catechism/artifact-builders/build.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
import { build as buildPathMap } from './path-map.ts';
import Catechism from '../content/catechism.json' assert { type: 'json' };
import { CatechismStructure } from '../source/types/types.ts';

import { build as buildContentMap } from './path-id-to-content-map.ts';
import { build as buildSemanticMap } from './semantic-path-to-renderable-path-id-map.ts';
import { build as buildTableOfContents } from './table-of-contents.ts';
import { Catechism } from '../source/catechism.ts';
import { PathMap, TableOfContentsType } from '../source/types/types.ts';
import { PathIdContentMap, SemanticPathPathIdMap, TableOfContentsType } from '../source/types/types.ts';

buildArtifacts();

function buildArtifacts(): void {
console.log('\nBuilding artifacts ...');

console.log('\ttable-of-contents ...');
const tableOfContents = buildTableOfContents(Catechism);
console.log('\ttable of contents ...');
const catechism = Catechism as CatechismStructure;
const tableOfContents = buildTableOfContents(catechism);
writeJson(tableOfContents, 'table-of-contents');

console.log('\tsemantic-path to path-id map ...');
const pathMap = buildPathMap(tableOfContents);
writeJson(pathMap, 'semantic-path-to-path-id');
console.log('\tSemanticPath to PathID map ...');
const renderablePathMap = buildSemanticMap(tableOfContents);
writeJson(renderablePathMap, 'semantic-path_to_renderable-path-id');

console.log('\tPathID to renderable PathID map ...');
const contentMap = buildContentMap(renderablePathMap, catechism);
writeJson(contentMap, 'renderable-path-id_to_content');
}

function writeJson(object: PathMap | TableOfContentsType, filename: string): void {
function writeJson(object: PathIdContentMap | SemanticPathPathIdMap | TableOfContentsType, filename: string): void {
Deno.writeTextFileSync(
`catechism/artifacts/${filename}.json`,
JSON.stringify(object, undefined, ' ') + '\n',
JSON.stringify(object, undefined, ' ') + '\n',
);
}
17 changes: 17 additions & 0 deletions catechism/artifact-builders/path-id-to-content-map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { CatechismStructure, PathIdContentMap, SemanticPathPathIdMap } from '../source/types/types.ts';
import { getContentForRendering } from '../../web-utils/rendering.ts';

export function build(renderablePathMap: SemanticPathPathIdMap, catechism: CatechismStructure): PathIdContentMap {
const contentMap: PathIdContentMap = {};

const pathIDs = Object.values(renderablePathMap);
for (const originalPathID of pathIDs) {
const content = getContentForRendering(originalPathID, catechism);

if (!contentMap[originalPathID]) {
contentMap[originalPathID] = content;
}
}

return contentMap;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PathMap, TableOfContentsEntry, TableOfContentsType } from '../source/types/types.ts';
import { SemanticPathPathIdMap, TableOfContentsEntry, TableOfContentsType } from '../source/types/types.ts';

export function build(tableOfContents: TableOfContentsType): PathMap {
export function build(tableOfContents: TableOfContentsType): SemanticPathPathIdMap {
const pathMaps = [
tableOfContents.prologue,
...tableOfContents.parts,
Expand All @@ -14,14 +14,14 @@ export function build(tableOfContents: TableOfContentsType): PathMap {
));
}

function entryToPathMaps(entry: TableOfContentsEntry): Array<PathMap> {
function entryToPathMaps(entry: TableOfContentsEntry): Array<SemanticPathPathIdMap> {
return [
getPathMap(entry),
getRenderablePathMap(entry),
...entry.children.flatMap((childEntry) => entryToPathMaps(childEntry)),
];
}

function getPathMap(entry: TableOfContentsEntry): PathMap {
function getRenderablePathMap(entry: TableOfContentsEntry): SemanticPathPathIdMap {
return {
[entry.semanticPath]: entry.pathID,
};
Expand Down
78 changes: 29 additions & 49 deletions catechism/artifact-builders/table-of-contents.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,58 @@
import { CatechismStructure } from '../source/types/catechism-structure.ts';
import {
Article,
ArticleParagraph,
buildSemanticPath,
Chapter,
CatechismStructure,
Content,
ContentBase,
ContentContainer,
Paragraph,
ParagraphGroup,
Part,
Section,
SemanticPathSource,
Subarticle,
TableOfContentsEntry,
TableOfContentsType,
} from '../source/types/types.ts';
import { getInBrief, getMainContent, getParagraphs } from '../utils.ts';
import { getFinalContent, getInBrief, getMainContent, getParagraphs } from '../source/utils/content.ts';
import { buildSemanticPath, getSemanticPathSource } from '../source/utils/semantic-path.ts';
import { getUrl } from '../../web-utils/routing.ts';

//#region builders
export function build(catechism: CatechismStructure): TableOfContentsType {
return {
prologue: buildEntry(catechism.prologue, [], true),
prologue: buildEntry(catechism.prologue, [], { forceIncludeChildren: true }),
parts: catechism.parts.map((part) => buildEntry(part, [])),
};
}

function buildEntry(
content: ContentBase | ContentContainer,
ancestors: Array<SemanticPathSource>,
forceIncludeChildren = false,
flags?: {
finalContent?: boolean;
forceIncludeChildren?: boolean;
},
): TableOfContentsEntry {
const semanticPathSource = getSemanticPathSource(content);
const { firstParagraphNumber, lastParagraphNumber } = getTerminalParagraphNumbers(content);

const isFinalContent = !!flags?.finalContent;

const semanticPathSource = getSemanticPathSource(content, isFinalContent);
const semanticPath = buildSemanticPath(semanticPathSource, ancestors);
const children = isFinalContent
? []
: buildChildEntries(content, [...ancestors, semanticPathSource], !!flags?.forceIncludeChildren);

return {
contentType: content.contentType,
title: getTitle(content),
title: getTitle(content, isFinalContent),
pathID: content.pathID,
semanticPath: buildSemanticPath(semanticPathSource, ancestors),
semanticPath,
url: getUrl(semanticPath),
firstParagraphNumber,
lastParagraphNumber,
children: buildChildEntries(content, [...ancestors, semanticPathSource], forceIncludeChildren),
children,
};
}

function buildChildEntries(
parent: ContentBase | ContentContainer,
ancestors: Array<SemanticPathSource>,
forceIncludeChildren = false,
forceIncludeChildren: boolean,
): Array<TableOfContentsEntry> {
const childEntries = getMainContent(parent)
.filter((child) => forceIncludeChildren || shouldGenerateChildEntry(parent, child))
Expand All @@ -59,6 +63,11 @@ function buildChildEntries(
childEntries.push(buildEntry(inBrief, ancestors));
}

const finalContent = getFinalContent(parent);
if (finalContent.length > 0) {
childEntries.push(buildEntry(finalContent[0], ancestors, { finalContent: true }));
}

return childEntries;
}
//#endregion
Expand All @@ -80,8 +89,8 @@ function shouldGenerateChildEntry(parent: ContentBase, child: ContentBase): bool
].some((validPairing) => parent.contentType === validPairing[0] && child.contentType === validPairing[1]);
}

function getTitle(content: ContentBase): string {
const number = getSemanticPathSource(content).number;
function getTitle(content: ContentBase, isFinalContent: boolean): string {
const number = getSemanticPathSource(content, isFinalContent).number;
const numberSuffix = number ? ` ${number}` : '';

// Replace underscores with spaces and implement title-casing
Expand All @@ -93,35 +102,6 @@ function getTitle(content: ContentBase): string {
.join(' ');
}

function getSemanticPathSource(content: ContentBase): SemanticPathSource {
return {
content: content.contentType,
number: getNumber(content),
};
}

function getNumber(content: ContentBase): number | null {
if (Content.PART === content.contentType) {
return (content as unknown as Part).partNumber;
} else if (Content.SECTION === content.contentType) {
return (content as unknown as Section).sectionNumber;
} else if (Content.CHAPTER === content.contentType) {
return (content as unknown as Chapter).chapterNumber;
} else if (Content.ARTICLE === content.contentType) {
return (content as unknown as Article).articleNumber;
} else if (Content.ARTICLE_PARAGRAPH === content.contentType) {
return (content as unknown as ArticleParagraph).articleParagraphNumber;
} else if (Content.SUB_ARTICLE === content.contentType) {
return (content as unknown as Subarticle).subarticleNumber;
} else if (Content.PARAGRAPH_GROUP === content.contentType) {
return (content as unknown as ParagraphGroup).paragraphGroupNumber;
} else if (Content.PARAGRAPH === content.contentType) {
return (content as unknown as Paragraph).paragraphNumber;
} else {
return null;
}
}

/**
* @returns the first and last `paragraphNumber` values of all the `Paragraph`s contained by the provided content
*/
Expand Down

0 comments on commit 4b6d2dd

Please sign in to comment.