Skip to content

Commit 150bdce

Browse files
committed
[CM2] Introduce ContentTypes and root.md
Now standalone content types can be stored in a schema folder (customizable via settings). For now, they have the content-modelling properties + a model property which points to a model. e.g. collection, post When a collection is matched with a content type, it inherits its properties. Also, collectionAliases can be put in a root.md to allow for more default collectionIndexFile names. Other content-modelling properties in root.md are skipped for now. With this change, now, the collection.md itself can be named in a domain-driven way. e.g. blog.md, portfolio.md, podcast.md Also: - Fix RegExpes that match indexFiles in models. #til: new RegExp(`\.`) and /\./ won't match the same thing.
1 parent ed20b8c commit 150bdce

File tree

9 files changed

+132
-35
lines changed

9 files changed

+132
-35
lines changed

src/compiler/contentModel2/index.js

+33-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
const _ = require('lodash')
21
const { resolve } = require('path')
2+
const _ = require('lodash')
3+
const frontMatter = require('front-matter')
34
const { isTemplateFile } = require('./helpers')
45
const models = {
56
homepage: require('./models/homepage'),
@@ -59,33 +60,54 @@ const defaultContentModelSettings = {
5960
homepageDirectory: 'homepage'
6061
}
6162
class ContentModel {
62-
constructor(contentModelSettings = defaultContentModelSettings) {
63+
constructor(contentModelSettings = defaultContentModelSettings, contentTypes = []) {
6364
this.settings = {
6465
...defaultContentModelSettings,
6566
...contentModelSettings
6667
}
68+
this.contentTypes = contentTypes
69+
}
70+
71+
create(fileSystemTree) {
72+
const indexFileNameOptions = ['root']
73+
74+
const isRootIndexFile = (node) => {
75+
return isTemplateFile(node) && node.name.match(
76+
new RegExp(`^(${indexFileNameOptions.join('|')})\\..+$`)
77+
)
78+
}
79+
80+
const indexFile = fileSystemTree.find(isRootIndexFile)
81+
const indexProps = indexFile ? frontMatter(indexFile.content) : {}
82+
83+
const context = {
84+
outputPath: this.settings.out,
85+
permalink: this.settings.permalinkPrefix
86+
}
6787

6888
this.models = {
6989
collection: models.collection({
70-
defaultCategoryName: this.settings.defaultCategoryName
71-
}),
90+
defaultCategoryName: this.settings.defaultCategoryName,
91+
collectionAliases: [
92+
...this.contentTypes
93+
.filter(ct => ct.model === 'collection')
94+
.map(ct => ct.collectionAlias),
95+
...(indexProps.attributes?.collectionAliases || [])
96+
]
97+
}, this.contentTypes),
98+
7299
subpage: models.subpage({
73100
pagesDirectory: this.settings.pagesDirectory
74101
}),
102+
75103
homepage: models.homepage({
76104
homepageDirectory: this.settings.homepageDirectory
77105
}),
106+
78107
asset: models.asset({
79108
assetsDirectory: this.settings.assetsDirectory
80109
})
81110
}
82-
}
83-
84-
create(fileSystemTree) {
85-
const context = {
86-
outputPath: this.settings.out,
87-
permalink: this.settings.permalinkPrefix
88-
}
89111

90112
const contentModel = {
91113
homepage: this.models.homepage.create({

src/compiler/contentModel2/models/collection/category.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ module.exports = function category(settings = defaultSettings) {
2323

2424
const isCategoryIndexFile = (node) => {
2525
return isTemplateFile(node) && node.name.match(
26-
new RegExp(`^${indexFileNameOptions.join('|')}\..+$`)
26+
new RegExp(`^(${indexFileNameOptions.join('|')})\\..+$`)
2727
)
2828
}
2929

src/compiler/contentModel2/models/collection/index.js

+31-18
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const { join, resolve } = require('path')
22
const _ = require('lodash')
33
const frontMatter = require('front-matter')
44
const makeSlug = require('slug')
5-
const { isTemplateFile, Markdown } = require('../../helpers')
5+
const { isTemplateFile, removeExtension, Markdown } = require('../../helpers')
66
const models = {
77
attachment: require('../attachment'),
88
category: require('./category'),
@@ -17,11 +17,19 @@ function parseContent(node, content) {
1717
}
1818

1919
const defaultSettings = {
20-
defaultCategoryName: 'Unclassified'
20+
defaultCategoryName: 'Unclassified',
21+
collectionAliases: []
2122
}
22-
module.exports = function collection(settings = defaultSettings) {
23+
module.exports = function collection(settings = defaultSettings, contentTypes = []) {
24+
const indexFileNameOptions = [
25+
...settings.collectionAliases,
26+
'collection'
27+
].filter(Boolean)
28+
2329
const isCollectionIndexFile = (node) => {
24-
return isTemplateFile(node) && node.name.match(/^collection\..+$/)
30+
return isTemplateFile(node) && node.name.match(
31+
new RegExp(`^(${indexFileNameOptions.join('|')})\\..+$`)
32+
)
2533
}
2634

2735
return {
@@ -85,45 +93,50 @@ module.exports = function collection(settings = defaultSettings) {
8593
}
8694

8795
const indexFile = node.children.find(isCollectionIndexFile)
88-
const indexProps = indexFile ? frontMatter(indexFile.content) : {}
96+
const indexProps = frontMatter(indexFile.content)
97+
const indexFileName = removeExtension(indexFile.name)
98+
99+
const contentType = contentTypes
100+
.filter(ct => ct.model === 'collection')
101+
.find(ct => ct.collectionAlias === indexFileName)
89102

90103
const slug = indexProps.attributes?.slug || makeSlug(node.name)
91104
const permalink = context.root.permalink + slug
92105
const outputPath = join(context.root.outputPath, slug)
93106
const collectionContext = {
94107
...indexProps.attributes,
95-
contentType: indexProps.attributes?.contentType || 'default',
96-
categoryContentType: indexProps.attributes?.categoryContentType || 'default',
97-
entryContentType: indexProps.attributes?.entryContentType || 'default',
98-
categoryAlias: indexProps.attributes?.categoryAlias,
99-
categoriesAlias: indexProps.attributes?.categoriesAlias,
100-
entryAlias: indexProps.attributes?.entryAlias,
101-
entriesAlias: indexProps.attributes?.entriesAlias,
102-
defaultCategoryName: indexProps.attributes?.defaultCategoryName || settings.defaultCategoryName,
108+
contentType: indexProps.attributes?.contentType || contentType?.name || 'default',
109+
categoryContentType: indexProps.attributes?.categoryContentType || contentType?.categoryContentType || 'default',
110+
entryContentType: indexProps.attributes?.entryContentType || contentType?.entryContentType || 'default',
111+
categoryAlias: indexProps.attributes?.categoryAlias || contentType?.categoryAlias,
112+
categoriesAlias: indexProps.attributes?.categoriesAlias || contentType?.categoriesAlias,
113+
entryAlias: indexProps.attributes?.entryAlias || contentType?.entryAlias,
114+
entriesAlias: indexProps.attributes?.entriesAlias || contentType?.entriesAlias,
115+
defaultCategoryName: indexProps.attributes?.defaultCategoryName || contentType?.defaultCategoryName || settings.defaultCategoryName,
103116
title: indexProps.attributes?.title || node.name,
104117
slug,
105118
permalink,
106119
outputPath
107120
}
108121

109-
const categoriesAlias = indexProps.attributes?.categoriesAlias
122+
const categoriesAlias = indexProps.attributes?.categoriesAlias || contentType?.categoriesAlias
110123
if (categoriesAlias) {
111124
tree[categoriesAlias] = tree.categories
112125
}
113126

114-
const entriesAlias = indexProps.attributes?.entriesAlias
127+
const entriesAlias = indexProps.attributes?.entriesAlias || contentType?.entriesAlias
115128
if (entriesAlias) {
116129
tree[entriesAlias] = tree.posts
117130
}
118131

119132
const childModels = {
120133
attachment: models.attachment(),
121134
category: models.category({
122-
categoryAlias: indexProps.attributes?.categoryAlias,
123-
entryAlias: indexProps.attributes?.entryAlias
135+
categoryAlias: indexProps.attributes?.categoryAlias || contentType?.categoryAlias,
136+
entryAlias: indexProps.attributes?.entryAlias || contentType?.entryAlias
124137
}),
125138
post: models.post({
126-
entryAlias: indexProps.attributes?.entryAlias
139+
entryAlias: indexProps.attributes?.entryAlias || contentType?.entryAlias
127140
})
128141
}
129142

src/compiler/contentModel2/models/collection/post.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ module.exports = function post(settings = defaultSettings) {
1515
return (
1616
isTemplateFile(node) &&
1717
node.name.match(
18-
new RegExp(`^${indexFileNameOptions.join('|')}\..+$`)
18+
new RegExp(`^(${indexFileNameOptions.join('|')})\\..+$`)
1919
)
2020
)
2121
}

src/compiler/contentModel2/models/homepage.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ module.exports = function homepage(settings = defaultSettings) {
1515
return (
1616
isTemplateFile(node) &&
1717
node.name.match(
18-
new RegExp(`^(${indexFileNameOptions.join('|')})\..+$`)
18+
new RegExp(`^(${indexFileNameOptions.join('|')})\\..+$`)
1919
)
2020
)
2121
}

src/compiler/contentModel2/models/subpage.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ module.exports = function subpage(settings = defaultSettings) {
1515
return (
1616
isTemplateFile(node) &&
1717
node.name.match(
18-
new RegExp(`^(${indexFileNameOptions.join('|')})\..+$`)
18+
new RegExp(`^(${indexFileNameOptions.join('|')})\\..+$`)
1919
)
2020
)
2121
}

src/content-types.js

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
const { readdir } = require('fs/promises')
2+
const { join } = require('path')
3+
const frontMatter = require('front-matter')
4+
const marked = require('marked')
5+
const { readFileContent } = require('./lib/fileSystemHelpers')
6+
7+
const defaultSettings = {
8+
rootDirectory: '.',
9+
contentTypesDirectory: 'schema'
10+
}
11+
const init = async (contentTypesSettings = defaultSettings) => {
12+
const { rootDirectory, contentTypesDirectory } = contentTypesSettings
13+
const contentTypesPath = join(
14+
rootDirectory,
15+
contentTypesSettings.contentTypesDirectory
16+
)
17+
const contentTypes = await readContentTypesDirectory(contentTypesPath)
18+
return contentTypes
19+
}
20+
21+
const readContentTypesDirectory = async (dir) => {
22+
const paths = await readdir(dir)
23+
return Promise.all(
24+
paths.map(async path => {
25+
const { attributes, content } = await readFile(join(dir, path))
26+
return {
27+
...attributes,
28+
description: content
29+
}
30+
})
31+
)
32+
}
33+
34+
const readFile = async (path) => {
35+
const rawContent = await readFileContent(path)
36+
const { attributes, body } = frontMatter(rawContent)
37+
const content = marked.parse(
38+
body.replace(/^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/, '')
39+
)
40+
return {
41+
attributes,
42+
content
43+
}
44+
}
45+
46+
module.exports = {
47+
init
48+
}

src/routines.js

+15-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const Expansions = require('./expansions')
99
const SiteDirectory = require('./site-directory')
1010
const CNAME = require('./cname')
1111
const Dictionary = require('./dictionary')
12+
const ContentTypes = require('./content-types')
1213
const FileSystemParser = require('./lib/FileSystemParser')
1314
const ContentModel1 = require('./compiler/contentModel')
1415
const ContentModel2 = require('./compiler/contentModel2')
@@ -70,6 +71,16 @@ const run = async ({ mode, rootDirectory, refreshTheme, finishCallback }) => {
7071
await SiteDirectory.create()
7172
await CNAME.create()
7273

74+
let contentTypes = []
75+
if (settings.compilerVersion === 2) {
76+
contentTypes = await ContentTypes.init(
77+
_.pick(settings, [
78+
'rootDirectory',
79+
'contentTypesDirectory'
80+
])
81+
)
82+
}
83+
7384
const logger = {
7485
debug: Debug.debugLog
7586
}
@@ -92,7 +103,8 @@ const run = async ({ mode, rootDirectory, refreshTheme, finishCallback }) => {
92103
'assetsDirectory',
93104
'pagesDirectory',
94105
'homepageDirectory'
95-
])
106+
]),
107+
contentTypes
96108
) :
97109
ContentModel1,
98110

@@ -107,7 +119,8 @@ const run = async ({ mode, rootDirectory, refreshTheme, finishCallback }) => {
107119
await finishCallback({
108120
settings,
109121
fileSystemTree,
110-
contentModel
122+
contentModel,
123+
contentTypes
111124
})
112125
}
113126

src/settings.js

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const defaultSettings = (rootDirectory) => ({
1212
theme: "default",
1313
permalinkPrefix: "/",
1414
defaultCategoryName: "Unclassified",
15+
contentTypesDirectory: "schema",
1516
assetsDirectory: "assets",
1617
exportDirectory: "docs",
1718
homepageDirectory: "homepage",

0 commit comments

Comments
 (0)