Skip to content

Commit 64bc8de

Browse files
committed
Add support for page.md
Docs
1 parent 1d404bc commit 64bc8de

File tree

10 files changed

+71
-17
lines changed

10 files changed

+71
-17
lines changed

.claude/settings.local.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(node --test:*)"
5+
]
6+
}
7+
}

README.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,12 @@ A `src` directory tree might look something like this:
7070
src % tree
7171
.
7272
├── md-page
73-
│ ├── README.md # directories with README.md in them turn into /md-page/index.html.
73+
│ ├── page.md # page.md (or README.md) in a directory turns into /md-page/index.html. page.md takes precedence.
7474
│ ├── client.ts # Every page can define its own client.ts script that loads only with it.
7575
│ ├── style.css # Every page can define its own style.css style that loads only with it.
7676
│ ├── loose-md-page.md # loose markdown get built in place, but lacks some page features.
7777
│ └── nested-page # pages are built in place and can nest.
78-
│ ├── README.md # This page is accessed at /md-page/nested-page/.
78+
│ ├── README.md # This page is accessed at /md-page/nested-page/. (page.md works here too)
7979
│ ├── style.css # nested pages are just pages, so they also can have a page scoped client and style.
8080
│ └── client.js # Anywhere JS loads, you can use .js or .ts
8181
├── html-page
@@ -109,7 +109,7 @@ src % tree
109109
│ ├── global.vars.ts # site wide variables get defined in global.vars.ts
110110
│ ├── markdown-it.settings.ts # You can customize the markdown-it instance used to render markdown
111111
│ └── esbuild.settings.ts # You can even customize the build settings passed to esbuild
112-
├── README.md # This is just a top level page built from a README.md file.
112+
├── page.md # The top level page can also be a page.md (or README.md) file.
113113
├── client.ts # the top level page can define a page scoped js client.
114114
├── style.css # the top level page can define a page scoped css style.
115115
└── favicon-16x16.png # static assets can live anywhere. Anything other than JS, CSS and HTML get copied over automatically.
@@ -165,13 +165,15 @@ Because pages are just directories, they nest and structure naturally as a files
165165
A `md` page looks like this on the filesystem:
166166

167167
```bash
168+
src/page-name/page.md
169+
# or
168170
src/page-name/README.md
169171
# or
170172
src/page-name/loose-md.md
171173
```
172174

173-
- `md` pages have two types: a `README.md` in a folder, or a loose `whatever-name-you-want.md` file.
174-
- `README.md` files transform to an `index.html` at the same path, and `whatever-name-you-want.md` loose markdown files transform into `whatever-name-you-want.html` files at the same path in the `dest` directory.
175+
- `md` pages have three types: a `page.md`, a `README.md`, or a loose `whatever-name-you-want.md` file.
176+
- `page.md` and `README.md` files transform to an `index.html` at the same path. When both exist in the same directory, `page.md` takes precedence over `README.md`. `whatever-name-you-want.md` loose markdown files transform into `whatever-name-you-want.html` files at the same path in the `dest` directory.
175177
- `md` pages can have YAML frontmatter, with variables that are accessible to the page layout and handlebars template blocks when building.
176178
- You can include html in markdown files, so long as you adhere to the allowable markdown syntax around html tags.
177179
- `md` pages support [handlebars][hb] template placeholders.
@@ -1250,7 +1252,7 @@ const layout: LayoutFunction<{site: string}, VDOMNode, string> = ({ children })
12501252
- Standardized entrypoints. Every page in a `domstack` site has a natural and obvious entrypoint. There is no magic redirection to learn about.
12511253
- Pages build into `index.html` files inside of named directories. This allows for naturally colocated assets next to the page, pretty URLs and full support for relative URLs.
12521254
- No parallel directory structures. You should never be forced to have two directories with identical layouts to put files next to each other. Everything should be colocatable.
1253-
- Markdown entrypoints are named README.md. This allows for the `src` folder to be fully navigable in GitHub and other git repo hosting providing a natural hosted CMS UI.
1255+
- Markdown entrypoints are named `page.md` or `README.md`. `README.md` allows for the `src` folder to be fully navigable in GitHub and other git repo hosting providing a natural hosted CMS UI. `page.md` is preferred when GitHub navigability is not a concern.
12541256
- Real TC39 ESM from the start.
12551257
- Garbage in, garbage out. Don't over-correct bad input.
12561258
- Conventions + standards. Vanilla file types. No new file extensions. No weird syntax to learn. Language tools should just work because you aren't doing anything weird or out of band.
@@ -1485,6 +1487,7 @@ Some notable features are included below, see the [roadmap](https://github.com/u
14851487
- [x] Rename to domstack
14861488
- [x] markdown-it.settings.ts support
14871489
- [x] page-worker.worker.ts page worker support
1490+
- [x] `page.md` page support
14881491
- ...[See roadmap](https://github.com/users/bcomnes/projects/3/)
14891492

14901493
## History

lib/build-pages/page-builders/md/get-md.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
/**
2-
*/
31
import markdownIt from 'markdown-it'
42
import markdownItFootnote from 'markdown-it-footnote'
53
import markdownItHighlightjs from 'markdown-it-highlightjs'
@@ -114,11 +112,11 @@ function rewriteLinks (body /*, pretty */) {
114112
return body.replace(regex, function (_match, p1, p2, _p3) {
115113
const f = p2.toLowerCase()
116114

117-
// root readme
118-
if (f === 'readme') return p1 + '/"'
115+
// root readme or page.md
116+
if (f === 'readme' || f === 'page') return p1 + '/"'
119117

120-
// nested readme
121-
if (f.match(/readme$/)) return p1 + f.replace(/readme$/, '') + '"'
118+
// nested readme or page.md
119+
if (f.match(/readme$/) || f.match(/\/page$/)) return p1 + f.replace(/(readme|page)$/, '') + '"'
122120

123121
// pretty url
124122
// if (pretty) return p1 + f + '/"'

lib/helpers/dom-stack-warning.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
* 'DOM_STACK_WARNING_DUPLICATE_GLOBAL_CLIENT' |
1111
* 'DOM_STACK_WARNING_DUPLICATE_ESBUILD_SETTINGS' |
1212
* 'DOM_STACK_WARNING_DUPLICATE_MARKDOWN_IT_SETTINGS' |
13-
* 'DOM_STACK_WARNING_DUPLICATE_GLOBAL_VARS'
13+
* 'DOM_STACK_WARNING_DUPLICATE_GLOBAL_VARS' |
14+
* 'DOM_STACK_WARNING_PAGE_MD_SHADOWS_README'
1415
* } DomStackWarningCode
1516
*/
1617

lib/identify-pages.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,11 @@ export async function identifyPages (src, opts = {}) {
240240
: files['page.html']
241241
if (htmlPage) htmlPage.type = 'html'
242242
/** @type {PageFile | undefined} */
243+
const pageMd = opts?.buildDrafts
244+
? files['page.md'] ?? files['page.draft.md']
245+
: files['page.md']
246+
if (pageMd) pageMd.type = 'md'
247+
/** @type {PageFile | undefined} */
243248
const readmePage = opts?.buildDrafts
244249
? files['README.md'] ?? files['README.draft.md']
245250
: files['README.md']
@@ -279,9 +284,16 @@ export async function identifyPages (src, opts = {}) {
279284
errors.push(err)
280285
}
281286

287+
if (pageMd && readmePage) {
288+
warnings.push({
289+
code: 'DOM_STACK_WARNING_PAGE_MD_SHADOWS_README',
290+
message: `${join(dir, 'page.md')} takes precedence over ${join(dir, 'README.md')}. Remove one to silence this warning.`,
291+
})
292+
}
293+
282294
const page = (conflict)
283295
? null
284-
: jsPage || htmlPage || readmePage
296+
: jsPage || htmlPage || pageMd || readmePage
285297

286298
if (page && page.type) {
287299
pages.push({
@@ -306,9 +318,10 @@ export async function identifyPages (src, opts = {}) {
306318
const isMarkdownFile = fileName.endsWith('.md')
307319
const isDraftFile = fileName.endsWith('.draft.md')
308320
const isReadmeFile = fileName === 'README.md' || fileName === 'README.draft.md'
321+
const isPageMdFile = fileName === 'page.md' || fileName === 'page.draft.md'
309322

310323
if (
311-
!isReadmeFile && (
324+
!isReadmeFile && !isPageMdFile && (
312325
(opts.buildDrafts && (isMarkdownFile || isDraftFile)) ||
313326
(!opts.buildDrafts && (isMarkdownFile && !isDraftFile))
314327
)

lib/identify-pages.test.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,16 @@ test.describe('identify-pages', () => {
1515
assert.ok(results.layouts, 'Layouts are found')
1616

1717
assert.ok(results.layouts['root'], 'A root layouts is found')
18-
assert.equal(Object.keys(results.pages).length, 25, '25 pages are found')
18+
assert.equal(Object.keys(results.pages).length, 27, '27 pages are found')
1919

20-
assert.equal(results.warnings.length, 0, '0 warnings produced')
20+
assert.equal(results.warnings.length, 1, '1 warning produced')
21+
assert.equal(results.warnings[0].code, 'DOM_STACK_WARNING_PAGE_MD_SHADOWS_README', 'page.md shadows README.md warning is produced')
2122
// assert.equal(results.nonPageFolders.length, 4, '4 non-page-folder')
2223
assert.equal(results.pages.find(p => p.path === 'html-page')?.pageFile?.type, 'html', 'html page is type html')
2324
assert.equal(results.pages.find(p => p.path === 'md-page')?.pageFile?.type, 'md', 'md page is type md')
2425
assert.equal(results.pages.find(p => p.path === 'js-page')?.pageFile?.type, 'js', 'js-page is type js')
26+
assert.equal(results.pages.find(p => p.path === 'page-md-page')?.pageFile?.type, 'md', 'page-md-page is type md')
27+
assert.equal(results.pages.find(p => p.path === 'page-md-page')?.pageFile?.basename, 'page.md', 'page-md-page uses page.md')
28+
assert.equal(results.pages.find(p => p.path === 'page-md-precedence')?.pageFile?.basename, 'page.md', 'page.md takes precedence over README.md')
2529
})
2630
})

test-cases/general-features/index.test.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,14 @@ test.describe('general-features', () => {
7474
style: true,
7575
worker: true
7676
},
77+
'page-md-page/index.html': {
78+
client: false,
79+
style: false,
80+
},
81+
'page-md-precedence/index.html': {
82+
client: false,
83+
style: false,
84+
},
7785
}
7886

7987
const files = await allFiles(dest, { shaper: fwData => fwData })
@@ -90,6 +98,17 @@ test.describe('general-features', () => {
9098
? 'Generated'
9199
: 'Did not generate'} a global client`)
92100

101+
// Special test for page.md precedence over README.md
102+
const pageMdPrecedencePath = path.join(dest, 'page-md-precedence/index.html')
103+
try {
104+
const pageMdContent = await readFile(pageMdPrecedencePath, 'utf8')
105+
assert.ok(pageMdContent.includes('from page.md'), 'page.md content is rendered')
106+
assert.ok(!pageMdContent.includes('from README.md'), 'README.md content is not rendered when page.md exists')
107+
} catch (err) {
108+
const error = err instanceof Error ? err : new Error('Unknown error', { cause: err })
109+
assert.fail('Failed to verify page.md precedence: ' + error.message)
110+
}
111+
93112
// Special test for markdown-it.settings.js
94113
const mdSettingsTestPath = path.join(dest, 'md-page/markdown-settings-test.html')
95114
try {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# page-md-page
2+
3+
This is an md page rendered from a page.md file.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# page-md-precedence (from README.md)
2+
3+
This content should NOT appear - page.md takes precedence.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# page-md-precedence (from page.md)
2+
3+
This content comes from page.md and should take precedence over README.md.

0 commit comments

Comments
 (0)