Move to custom SSG instead of eleventy.

This commit is contained in:
Joshua Bemenderfer 2023-03-04 22:36:08 -05:00
parent 31bb42e985
commit f42225bd13
59 changed files with 337 additions and 1784 deletions

View File

@ -1,52 +0,0 @@
const EleventyVitePlugin = require('@11ty/eleventy-plugin-vite')
const EleventyGoogleFonts = require("eleventy-google-fonts");
const EleventyFeatherIcons = require('eleventy-plugin-feathericons');
const HighlightJS = require('highlight.js')
const { useDocument } = require('@terrace-lang/js/document')
const { createFileReader } = require('@terrace-lang/js/readers/node-readline')
const parsePage = require('./src/parser/page.js');
module.exports = function (config) {
config.addPlugin(EleventyVitePlugin)
config.addPlugin(EleventyGoogleFonts)
config.addPlugin(EleventyFeatherIcons)
config.addPassthroughCopy('src/public')
config.addPassthroughCopy('src/styles')
config.addPassthroughCopy('src/main.js')
config.addTemplateFormats('tce')
config.addExtension('tce', {
async compile(content) {
return async () => content
},
getData(inputPath) {
const doc = useDocument(createFileReader(inputPath))
return parsePage(doc)
}
})
HighlightJS.registerLanguage('terrace', () => ({
name: 'Terrace',
contains: [
{
className: 'keyword',
begin: /^\s*(.*?)(?:\s|$)/,
relevance: 1
}
]
}))
config.addFilter("highlight", function(value, language) {
return HighlightJS.highlight(value, { language }).value
})
return {
dir: {
input: 'src',
output: '_site'
},
passthroughFileCopy: true
}
}

View File

@ -1 +0,0 @@
/**/*.inc.tce

2
docs/.gitignore vendored
View File

@ -1 +1 @@
_site
dist/

View File

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -1,6 +1,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "./highlightjs-theme.css";
details summary {
list-style: none;

View File

@ -2,22 +2,29 @@
"name": "@terrace-lang/docs",
"private": true,
"license": "MIT",
"type": "module",
"scripts": {
"dev": "eleventy --serve --incremental",
"build": "eleventy"
"dev": "nodemon -e js,njk,html,css --ignore dist ./renderer/render.js & tailwindcss -i ./main.css -o ./dist/main.css --watch",
"server": "browser-sync start --server ./dist --files './dist/**/*'",
"build": "node ./renderer/render.js && tailwindcss -i ./main.css -o ./dist/main.css"
},
"exports": {
"./read-page": {
"default": "./read-page/index.js"
}
},
"devDependencies": {
"@11ty/eleventy": "^2.0.0",
"@11ty/eleventy-plugin-vite": "^4.0.0",
"@sindresorhus/slugify": "^2.2.0",
"@tailwindcss/typography": "^0.5.9",
"@terrace-lang/js": "workspace:*",
"autoprefixer": "^10.4.13",
"eleventy-google-fonts": "^0.1.0",
"eleventy-plugin-feathericons": "^1.0.1",
"browser-sync": "^2.28.3",
"browsersync": "0.0.1-security",
"highlight.js": "^11.7.0",
"marked": "^4.2.12",
"tailwindcss": "^3.2.6",
"vite": "^3.2.3"
"feather-icons": "^4.29.0",
"nodemon": "^2.0.21",
"nunjucks": "^3.2.3"
}
}

View File

@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

View File

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -0,0 +1 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#c678dd}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#98c379}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#d19a66}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#61aeee}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#e6c07b}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}

View File

@ -1,5 +1,5 @@
module.exports.contentAsText = async function(doc, rootLevel, includeCurrent = false) {
export async function contentAsText (doc, rootLevel, includeCurrent = false) {
const { level, next, line, head } = doc
const linesAsArray = []
if (includeCurrent) linesAsArray.push(line())

View File

@ -1,10 +1,14 @@
const knownNodes = require('./nodes/index.js')
import knownNodes from './nodes/index.js'
import { useDocument } from '@terrace-lang/js'
import { createFileReader } from '@terrace-lang/js/readers/node-readline'
module.exports = async function(doc) {
export default async function(inputPath) {
const doc = useDocument(createFileReader(inputPath))
const { next, line, match, tail, level, head } = doc
const pageData = {
type: `Page`,
path: inputPath,
title: '',
description: [],
layout: '',

View File

@ -1,6 +1,6 @@
const { contentAsText } = require('../helpers.js')
import { contentAsText } from '../helpers.js'
module.exports = async function (doc, rootLevel) {
export default async function (doc, rootLevel) {
const { next, line, match, tail, level, head } = doc
const node = {

View File

@ -1,6 +1,6 @@
const { contentAsText } = require('../helpers')
import { contentAsText } from '../helpers.js'
module.exports = async (doc, rootLevel) => {
export default async (doc, rootLevel) => {
const { next, level, line, head, tail, match } = doc
const node = {

View File

@ -1,8 +1,8 @@
const { contentAsText } = require('../helpers')
import { contentAsText } from '../helpers.js'
const languages = ['terrace', 'json', 'yaml', 'toml', 'javascript', 'typescript', 'c', 'python', 'sh']
module.exports = async (doc, rootLevel) => {
export default async (doc, rootLevel) => {
const { next, level, line, head, tail, match } = doc
const node = {

View File

@ -1,5 +1,5 @@
const { contentAsText } = require('../helpers.js')
const marked = require('marked')
import { contentAsText } from '../helpers.js'
import { parse } from 'marked'
const FOOTER_TEXT = `
Maintained by the Terrace Team. Find an issue? [Let us know](/issues)!
@ -8,13 +8,13 @@ Site contents licensed under the [CC BY 3.0 license](https://creativecommons.org
All code examples licensed under the [MIT license](https://opensource.org/licenses/MIT)
`
module.exports = async function (doc, rootLevel) {
export default async function (doc, rootLevel) {
const { next, line, match, tail, level, head } = doc
const node = {
type: `Markdown`,
class: 'text-center mt-32 mx-auto text-neutral-50/75 prose-a:text-primary-100/75',
text: marked.parse(FOOTER_TEXT)
text: parse(FOOTER_TEXT)
}
return node

View File

@ -1,7 +1,6 @@
import slugify from '@sindresorhus/slugify'
module.exports = async function (doc, rootLevel, pageData) {
const slugify = (await import('@sindresorhus/slugify')).default
export default async function (doc, rootLevel, pageData) {
const { next, line, match, tail, level, head } = doc
const headingLevel = +tail().split(' ')[0]

View File

@ -1,4 +1,4 @@
module.exports = async function (doc) {
export default async function (doc) {
const { head, tail } = doc
const node = {

View File

@ -1,10 +1,10 @@
const { useDocument } = require('@terrace-lang/js/document')
const { createFileReader } = require('@terrace-lang/js/readers/node-readline')
const fs = require('fs/promises')
const path = require('path')
const knownNodes = require('./index.js')
import { useDocument } from '@terrace-lang/js/document'
import { createFileReader } from '@terrace-lang/js/readers/node-readline'
import fs from 'fs/promises'
import path from 'path'
import knownNodes from './index.js'
module.exports = async function (originalDoc, rootLevel, ...args) {
export default async function (originalDoc, rootLevel, ...args) {
const includedDoc = useDocument(createFileReader(originalDoc.tail()))
const { next, head, tail, level } = includedDoc

View File

@ -1,6 +1,6 @@
const { contentAsText } = require('../helpers.js')
import { contentAsText } from '../helpers.js'
module.exports = async function (doc, rootLevel) {
export default async function (doc, rootLevel) {
const { next, line, match, tail, level, head } = doc
const node = {

View File

@ -1,7 +1,7 @@
const { contentAsText } = require('../helpers.js')
const marked = require('marked')
import { contentAsText } from '../helpers.js'
import { parse } from 'marked'
module.exports = async function (doc, rootLevel) {
export default async function (doc, rootLevel) {
const { next, line, match, tail, level, head } = doc
const node = {
@ -12,7 +12,7 @@ module.exports = async function (doc, rootLevel) {
while (await next(rootLevel)) {
if (match('class')) node.class = tail()
else node.text = marked.parse(await contentAsText(doc, rootLevel, true))
else node.text = parse(await contentAsText(doc, rootLevel, true))
}
return node

View File

@ -1,6 +1,6 @@
const knownNodes = require('./index.js')
import knownNodes from './index.js'
module.exports = async function (doc, rootLevel, ...args) {
export default async function (doc, rootLevel, ...args) {
const { next, line, match, tail, level, head } = doc
const node = {

View File

@ -1,4 +1,4 @@
module.exports = async function (doc, rootLevel) {
export default async function (doc, rootLevel) {
const { next, head, tail, match } = doc
const node = {

View File

@ -0,0 +1,32 @@
import parseNode from './Node.js'
import Include from './Include.js'
import TableOfContents from './TableOfContents.js'
import Heading from './Heading.js'
import Button from './Button.js'
import Icon from './Icon.js'
import Markdown from './Markdown.js'
import CodeBlock from './CodeBlock.js'
import CodeExample from './CodeExample.js'
import Logo from './Logo.js'
import Footer from './Footer.js'
const Block = parseNode
const Section = async (doc, rootLevel, ...args) => {
const variant = doc.tail()
return { variant, ...(await parseNode(doc, rootLevel, ...args)) }
}
export default {
Include,
TableOfContents,
Block,
Section,
Heading,
Button,
Icon,
Markdown,
CodeBlock,
CodeExample,
Logo,
Footer
}

20
docs/renderer/layout.njk Normal file
View File

@ -0,0 +1,20 @@
<!doctype html>
{% from "nodes/Node.njk" import Node %}
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ page.title }}</title>
<meta name="description" content="{{ page.description | join(' ') }}"/>
<link rel="stylesheet" href="/main.css"/>
<link rel="stylesheet" href="/public/styles/highlightjs-theme.css"/>
{{ googleFonts('https://fonts.googleapis.com/css2?family=Fredoka:wght@300;400;500&display=swap') }}
</head>
<body class="text-base pt-16">
{{ Node('Navbar', {}, { headings: page.headings, url: url }) }}
{% for child in page.children %}
{{ Node(child.type, child, { headings: page.headings, url: url }) }}
{% endfor %}
</body>
</html>

View File

@ -1,3 +1,3 @@
{% macro render(node) %}
{% feather node.icon %}
{{ featherIcons(node.icon) }}
{% endmacro %}

View File

@ -1,12 +1,12 @@
{% macro render(node) %}
{% if node.variant == 'small' %}
<div class="flex gap-2 items-center {{node.class}}">
<img src="/logo.png" class="w-8 h-8" alt=""/>
<img src="/public/logo.png" class="w-8 h-8" alt=""/>
<span class="text-3xl text-transparent bg-clip-text bg-gradient-to-b from-primary-400 to-primary-600">Terrace</span>
</div>
{% else %}
<div class="flex gap-4 items-center {{node.class}}">
<img src="/logo.png" class="w-8 h-8 md:w-16 md:h-16" alt=""/>
<img src="/public/logo.png" class="w-8 h-8 md:w-16 md:h-16" alt=""/>
<h1 class="text-xl md:text-5xl text-transparent bg-clip-text bg-gradient-to-b from-primary-400 to-primary-600">Terrace</h1>
</div>
{% endif %}

50
docs/renderer/render.js Normal file
View File

@ -0,0 +1,50 @@
import fs from 'node:fs/promises'
import path from 'node:path'
import nunjucks from 'nunjucks'
import HighlightJS from 'highlight.js'
import readPage from '../read-page/index.js'
import googleFonts from './util/google-fonts.js'
import featherIcons from './util/feather-icons.js'
const pages = {
'/': './pages/index.tce',
'/about/': './pages/about.tce'
}
async function render() {
// await fs.rm('dist', { recursive: true })
for (const [urlPath, filePath] of Object.entries(pages)) {
const env = nunjucks.configure('renderer/', { autoescape: false })
env.addGlobal('googleFonts', googleFonts)
env.addGlobal('featherIcons', featherIcons)
HighlightJS.registerLanguage('terrace', () => ({
name: 'Terrace',
contains: [
{
className: 'keyword',
begin: /^\s*(.*?)(?:\s|$)/,
relevance: 1
}
]
}))
env.addFilter("highlight", (value, language) => {
return HighlightJS.highlight(value, { language }).value
})
const page = await readPage(filePath)
const result = env.render('layout.njk', { page, url: urlPath, file: filePath })
await fs.mkdir(path.join('dist', urlPath), { recursive: true })
await fs.writeFile(path.join('dist', urlPath, 'index.html'), result)
}
// await fs.cp('node_modules/highlight.js/styles/atom-one-dark.css', '');
await fs.cp('./public/', './dist/public/', { recursive: true })
await fs.cp('./favicon.ico', './dist/favicon.ico', { recursive: true })
}
render()

View File

@ -0,0 +1,19 @@
import feather from 'feather-icons'
const defaultAttributes = {
"class": "feather feather-x",
"xmlns": "http://www.w3.org/2000/svg",
"width": 24,
"height": 24,
"viewBox": "0 0 24 24",
"fill": "none",
"stroke": "currentColor",
"stroke-width": 2,
"stroke-linecap": "round",
"stroke-linejoin": "round",
}
export default (iconName, attributes = {}) => {
attributes = { ...defaultAttributes, ...attributes }
return feather.icons[iconName].toSvg(attributes)
}

View File

@ -0,0 +1,45 @@
import https from 'node:https'
const UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36'
const isValidURL = url => {
return /fonts.googleapis.com/.test(url)
}
const downloadFont = url => {
return new Promise((resolve) => {
let rawData = ''
https.get(
url,
{
headers: {
'user-agent': UA,
},
},
res => {
res.on('data', chunk => {
rawData += chunk
})
res.on('end', () => {
resolve(rawData.toString('utf8'))
})
}
)
})
}
const createInlineCss = async url => {
if (!isValidURL(url)) {
throw new Error('Invalid Google Fonts URL')
}
const content = await downloadFont(url)
return (
`<link rel="preconnect" href="https://fonts.gstatic.com">`+
`<link data-href="${url}" rel="stylesheet">`+
`<style data-href='${url}'>${content}</style>`
)
}
export default createInlineCss

View File

@ -1,19 +0,0 @@
<!doctype html>
{% from "nodes/Node.njk" import Node %}
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }}</title>
<meta name="description" content="{{ description | join(' ') }}"/>
{% eleventyGoogleFonts 'https://fonts.googleapis.com/css2?family=Fredoka:wght@300;400;500&display=swap' %}
</head>
<body class="text-base pt-16">
{{ Node('Navbar', {}, { headings: headings, url: page.url }) }}
{% for child in children %}
{{ Node(child.type, child, { headings: headings, url: page.url }) }}
{% endfor %}
<script type="module" src="/main.js"></script>
</body>
</html>

View File

@ -1,2 +0,0 @@
import './styles/main.css'
import 'highlight.js/styles/atom-one-dark.css'

View File

@ -1,19 +0,0 @@
const parseNode = require('./Node.js')
module.exports.Include = require('./Include.js')
module.exports.Block = parseNode
module.exports.Section = async (doc, rootLevel, ...args) => {
const variant = doc.tail()
return { variant, ...(await parseNode(doc, rootLevel, ...args)) }
}
module.exports.TableOfContents = require('./TableOfContents.js')
module.exports.Heading = require('./Heading.js')
module.exports.Button = require('./Button.js')
module.exports.Icon = require('./Icon.js')
module.exports.Markdown = require('./Markdown.js')
module.exports.CodeBlock = require('./CodeBlock.js')
module.exports.CodeExample = require('./CodeExample.js')
module.exports.Logo = require('./Logo.js')
module.exports.Footer = require('./Footer.js')

View File

@ -2,7 +2,7 @@ const defaultTheme = require('tailwindcss/defaultTheme');
const colors = require('tailwindcss/colors')
module.exports = {
content: ['./src/**/*.js', './src/**/*.njk', './src/**/*.tce'],
content: ['./read-page/**/*.js', './renderer/**/*.njk', './**/*.tce'],
theme: {
colors: {
...colors,

View File

@ -1,3 +0,0 @@
import { defineConfig } from 'vite';
export default defineConfig()

View File

View File

@ -3,6 +3,7 @@
"description": "Terrace is a simple structured data syntax for configuration, content authoring, and DSLs.",
"version": "0.1.0",
"license": "MIT",
"type": "module",
"repository": {
"type": "git",
"url": "https://github.com/terrace-lang/terrace.git",
@ -16,6 +17,11 @@
"types": "./dist/types/*.d.ts",
"import": "./dist/esm/*.js",
"require": "./dist/cjs/*.js"
},
".": {
"types": "./dist/types/index.d.ts",
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js"
}
},
"scripts": {

1747
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff