Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: marked extensions #230

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/nuekit/src/nuefs.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export async function fswalk(root, _dir='', _ret=[]) {
return _ret
}

const IGNORE = ['node_modules', 'functions', 'package.json', 'bun.lockb', 'pnpm-lock.yaml']
const IGNORE = ['node_modules', 'functions', 'package.json', 'bun.lockb', 'pnpm-lock.yaml', 'yarn.lock', 'package-lock.json', 'marked.config.js']

function ignore(name='') {
return '._'.includes(name[0]) || IGNORE.includes(name)
Expand Down
4 changes: 2 additions & 2 deletions packages/nuekit/src/nuekit.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export async function createKit(args) {
// site: various file based functions
const site = await createSite(args)

const { dist, port, read, copy, write, is_empty } = site
const { dist, port, read, copy, write, is_empty, marked_extensions } = site
const is_dev = !is_prod

// make sure @nue dir has all the latest
Expand Down Expand Up @@ -134,7 +134,7 @@ export async function createKit(args) {
const dir = data.appdir || file.dir
const lib = await site.getLayoutComponents(dir)

data.content = renderPage(data.page, { data, lib }).html
data.content = renderPage(data.page, { data, lib, marked_extensions }).html

function render(name, def) {
const layout = lib.find(el => el.tagName == name) || def && parseNue(def)[0]
Expand Down
5 changes: 4 additions & 1 deletion packages/nuekit/src/site.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ export async function createSite(args) {
self.is_empty = true
}

const markedConfig = joinRootPath(root, 'marked.config.js', true)
const { default: marked_extensions=[] } = await import(markedConfig).catch(() => ({}))
nobkd marked this conversation as resolved.
Show resolved Hide resolved

async function write(content, dir, filename) {
const todir = join(dist, dir)

Expand Down Expand Up @@ -301,6 +304,6 @@ export async function createSite(args) {
}
}

return { ...self, dist, port, read, write, copy }
return { ...self, dist, port, read, write, copy, marked_extensions }

}
23 changes: 22 additions & 1 deletion packages/nuekit/test/nuekit.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ test('content collection', async () => {
// 5. System files starting with '_' or '.' are excluded.
await write('blog/.item6.md', createFront('Sixth', '2020-01-03'))
await write('blog/_item7.md', createFront('Seventh', '2020-01-03'))

const site = await getSite()
const coll = await site.getContentCollection('blog')
const actual = coll.map(c => {
Expand Down Expand Up @@ -328,3 +328,24 @@ test('the project was started for the first time', async () => {
terminate()
}
})

test('marked extension config file', async() => {
const marked_config = `export default [{
tokenizer: {
inlineText(src) {
const cap = this.rules.inline.text.exec(src)
const text = cap[0].replace(/\\.{3}/g, '\\u2026')
return { type: 'text', raw: cap[0], text }
}
}
}]`

await write('marked.config.js', marked_config)
await write('index.md', 'This is a test, right?\n\n...\n\nRight?\n\n.....')

const kit = await getKit()
const html = await kit.gen('index.md')

const ellipsis = '…'
expect(html).toInclude(`<p>This is a test, right?</p>\n<p>${ellipsis}</p>\n<p>Right?</p>\n<p>${ellipsis}..</p>\n`)
})
58 changes: 28 additions & 30 deletions packages/nuemark/src/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,41 @@ import { parsePage, parseHeading } from './parse.js'
import { parseAttr } from './component.js'
import { marked } from 'marked'

let exts_loaded = false

// marked renderers
const renderer = {

heading(html, level, raw) {
const plain = parseHeading(raw)
const cls = plain.class
const title = plain.text.replaceAll('"', '')
const rich = parseHeading(html)

delete plain.text
const a = elem('a', { href: `#${plain.id}`, title: title })
return elem(`h${level}`, plain, a + rich.text)
},

// lazyload images by default
image(src, title, alt) {
return elem('img', { src, title, alt, loading: 'lazy' })
},
}

marked.use({ renderer })

export function renderPage(page, opts) {
const { lib=[] } = opts
const { lib=[], marked_extensions=[] } = opts
const data = { ...opts.data, ...page.meta }
const draw_sections = data?.draw_sections || page.sections[1]
const section_attr = data.sections || []
const ret = []

if (!exts_loaded && marked_extensions.length) {
exts_loaded = true
marked.use(...marked_extensions)
}

// section_attr
page.sections.forEach((section, i) => {
Expand Down Expand Up @@ -94,32 +121,3 @@ function parseLink(href) {
if (title) href = href.slice(0, i)
return { href, title }
}

marked.setOptions({
smartypants: true,
headerIds: false,
smartLists: false,
mangle: false
})

// marked renderers
const renderer = {

heading(html, level, raw) {
const plain = parseHeading(raw)
const cls = plain.class
const title = plain.text.replaceAll('"', '')
const rich = parseHeading(html)

delete plain.text
const a = elem('a', { href: `#${plain.id}`, title: title })
return elem(`h${level}`, plain, a + rich.text)
},

// lazyload images by default
image(src, title, alt) {
return elem('img', { src, title, alt, loading: 'lazy' })
},
}

marked.use({ renderer })
27 changes: 22 additions & 5 deletions packages/nuemark/test/nuemark.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ test('[layout]', () => {
expect(single).toInclude('<p>foo</p>')

const double = tags.layout({ attr, data, content: ['foo', 'bar'] })
expect(double).toInclude('<section id="epic">')
expect(double).toInclude('<section id="epic">')
})

test('[layout] with nested component', () => {
Expand Down Expand Up @@ -376,7 +376,7 @@ test('parseSpecs', () => {
})

test('parse plain args', () => {
const { name, data }= parseComponent('video src="/a.mp4" loop muted')
const { name, data } = parseComponent('video src="/a.mp4" loop muted')
expect(name).toBe('video')
expect(data.loop).toBe(true)
expect(data.muted).toBe(true)
Expand Down Expand Up @@ -422,6 +422,23 @@ test('parseComponent', () => {

})

test('marked extension', async () => {
const ellipsis = '\u2026'
const marked_extensions = [{
tokenizer: {
inlineText(src) {
const cap = this.rules.inline.text.exec(src)
const text = cap[0].replace(/\.{3}/g, ellipsis)
return { type: 'text', raw: cap[0], text }
}
}
}]

const { html } = renderLines(['This is a test, right?\n', '...\n', 'Right?\n', '.....'], { marked_extensions })

expect(html).toBe(`<p>This is a test, right?</p>\n<p>${ellipsis}</p>\n<p>Right?</p>\n<p>${ellipsis}..</p>\n`)
})

/*
Required:
bun add react
Expand All @@ -437,16 +454,16 @@ test('JSX component', async () => {

// make them compatible with Nuemark
const lib = Object.keys(jsx).map(name => {
return { name, render: (data) => renderToString(jsx[name](data)) }
return { name, render: (data) => renderToString(jsx[name](data)) }
})

// render JSX with Nuemark
const html = nuemarkdown('[my-test]', { lib, data: { message: 'Hello' } })

expect(html).toBe('<h1 style="color:red">Hello</h1>')

// react not imported
// react not imported
} catch (ignored) {
console.info('JSX test skipped')
console.info('JSX test skipped')
}
})