diff --git a/.changeset/dirty-mugs-bow.md b/.changeset/dirty-mugs-bow.md new file mode 100644 index 0000000..9a1ce1a --- /dev/null +++ b/.changeset/dirty-mugs-bow.md @@ -0,0 +1,6 @@ +--- +"@kopflos-cms/plugin-deploy-resources": minor +"@kopflos-cms/plugin-express": minor +--- + +First version diff --git a/.changeset/dry-moose-applaud.md b/.changeset/dry-moose-applaud.md new file mode 100644 index 0000000..6698c17 --- /dev/null +++ b/.changeset/dry-moose-applaud.md @@ -0,0 +1,6 @@ +--- +"@kopflos-cms/core": patch +--- + +Extracted logger to a new package `@kopflos-cms/core` + diff --git a/.changeset/five-avocados-impress.md b/.changeset/five-avocados-impress.md new file mode 100644 index 0000000..c9ac912 --- /dev/null +++ b/.changeset/five-avocados-impress.md @@ -0,0 +1,6 @@ +--- +"@kopflos-cms/core": patch +"@kopflos-cms/express": patch +--- + +Support for direct stream, using Web Streams diff --git a/.changeset/friendly-geese-eat.md b/.changeset/friendly-geese-eat.md new file mode 100644 index 0000000..b47c93f --- /dev/null +++ b/.changeset/friendly-geese-eat.md @@ -0,0 +1,5 @@ +--- +"@kopflos-cms/core": patch +--- + +Revert dependency on `anylogger` to stable v1 branch diff --git a/.changeset/gentle-apes-serve.md b/.changeset/gentle-apes-serve.md new file mode 100644 index 0000000..784ecb3 --- /dev/null +++ b/.changeset/gentle-apes-serve.md @@ -0,0 +1,5 @@ +--- +"@kopflos-cms/vite": patch +--- + +Ensure the the `outDir` setting is also used for serving files in in production mode diff --git a/.changeset/odd-hounds-float.md b/.changeset/odd-hounds-float.md new file mode 100644 index 0000000..e87f689 --- /dev/null +++ b/.changeset/odd-hounds-float.md @@ -0,0 +1,30 @@ +--- +"@kopflos-cms/core": patch +--- + +Added support for templated resource shapes. Use `kl:regex` a pattern to match the request URL path. +Additionally, named capturing groups can be used to extract values from the URL path. They will be +accessible as `HandlerArgs#subjectVariables` and included when resolving `code:EcmaScriptTemplateLiteral`. + +```turtle +<#WebPage> + a kl:ResourceShape ; + kl:api <> ; + sh:target + [ + a kl:PatternedTarget ; + kl:regex "/(?[^/]+).+\\.html$" ; + ] ; + kl:handler + [ + a kl:Handler ; + kl:method "GET" ; + code:implementedBy + [ + a code:EcmaScriptModule ; + code:link ; + code:arguments ( "pages/${type}.html"^^code:EcmaScriptTemplateLiteral ) ; + ] ; + ] ; +. +``` diff --git a/.changeset/pink-eggs-collect.md b/.changeset/pink-eggs-collect.md new file mode 100644 index 0000000..14f8bd2 --- /dev/null +++ b/.changeset/pink-eggs-collect.md @@ -0,0 +1,29 @@ +--- +"@kopflos-cms/core": minor +--- + +Handlers: Added support for `code:arguments`. Please refer to [rdf-loader-code](https://github.com/zazuko/rdf-loader-code?tab=readme-ov-file#loading-function-arguments) for more information. + +```turtle +[ + a kl:Handler ; + code:implementedBy [ + a code:EcmaScriptModule ; + code:link <...> ; + code:arguments ("foo" "bar") ; + ] ; +] . +``` + +Implementors must now return a factory function that returns the handler function. + +```diff +import type { Handler } from "@kopflos-cms/core"; + +- export default function handler() { ++ export default function handler(foo, bar): Handler { + return async function handlerFunction() { + // ... + }; +} +``` diff --git a/.changeset/real-numbers-visit.md b/.changeset/real-numbers-visit.md new file mode 100644 index 0000000..61a2a57 --- /dev/null +++ b/.changeset/real-numbers-visit.md @@ -0,0 +1,5 @@ +--- +"@kopflos-cms/express": minor +--- + +The main export now returns `Promise<{ middleware: RequestHandler; instance: Kopflos }>` diff --git a/.changeset/red-bottles-drum.md b/.changeset/red-bottles-drum.md new file mode 100644 index 0000000..32a5a91 --- /dev/null +++ b/.changeset/red-bottles-drum.md @@ -0,0 +1,5 @@ +--- +"@kopflos-cms/serve-file": minor +--- + +First version: serve files directly, with an option to stream directly diff --git a/.changeset/shy-rivers-smoke.md b/.changeset/shy-rivers-smoke.md new file mode 100644 index 0000000..9ad2ff4 --- /dev/null +++ b/.changeset/shy-rivers-smoke.md @@ -0,0 +1,5 @@ +--- +"kopflos": patch +--- + +Added `--variable ` option to override config variables diff --git a/.changeset/sixty-suns-melt.md b/.changeset/sixty-suns-melt.md new file mode 100644 index 0000000..e90e468 --- /dev/null +++ b/.changeset/sixty-suns-melt.md @@ -0,0 +1,6 @@ +--- +"@kopflos-labs/html-template": minor +"@kopflos-labs/handlebars": minor +--- + +First version diff --git a/.changeset/stupid-ads-destroy.md b/.changeset/stupid-ads-destroy.md new file mode 100644 index 0000000..afce2de --- /dev/null +++ b/.changeset/stupid-ads-destroy.md @@ -0,0 +1,7 @@ +--- +"@kopflos-cms/express": patch +"@kopflos-cms/core": patch +"kopflos": patch +--- + +Added plugin with `onStart` hook diff --git a/.changeset/tame-cobras-leave.md b/.changeset/tame-cobras-leave.md new file mode 100644 index 0000000..060c713 --- /dev/null +++ b/.changeset/tame-cobras-leave.md @@ -0,0 +1,6 @@ +--- +"@kopflos-cms/plugin-express": minor +"@kopflos-cms/express": minor +--- + +Added express-only middleware hooks diff --git a/.changeset/tender-countries-worry.md b/.changeset/tender-countries-worry.md new file mode 100644 index 0000000..5f2bfa0 --- /dev/null +++ b/.changeset/tender-countries-worry.md @@ -0,0 +1,5 @@ +--- +"sparql-path-parser": minor +--- + +First version diff --git a/.changeset/thick-socks-juggle.md b/.changeset/thick-socks-juggle.md new file mode 100644 index 0000000..239bde6 --- /dev/null +++ b/.changeset/thick-socks-juggle.md @@ -0,0 +1,5 @@ +--- +"@kopflos-labs/lit": minor +--- + +First version - Server-Side rendering diff --git a/.changeset/thin-buses-remember.md b/.changeset/thin-buses-remember.md new file mode 100644 index 0000000..5bb96a3 --- /dev/null +++ b/.changeset/thin-buses-remember.md @@ -0,0 +1,5 @@ +--- +"kopflos": minor +--- + +First version diff --git a/.changeset/thin-penguins-greet.md b/.changeset/thin-penguins-greet.md new file mode 100644 index 0000000..a03c3e1 --- /dev/null +++ b/.changeset/thin-penguins-greet.md @@ -0,0 +1,5 @@ +--- +"@kopflos-cms/core": patch +--- + +Added `./env.js` to package exports diff --git a/.changeset/tiny-eels-smoke.md b/.changeset/tiny-eels-smoke.md new file mode 100644 index 0000000..4fc49bf --- /dev/null +++ b/.changeset/tiny-eels-smoke.md @@ -0,0 +1,5 @@ +--- +"@kopflos-cms/express": patch +--- + +Ensure that middlewares from multiple plugins are registered in order diff --git a/.changeset/tough-peas-wink.md b/.changeset/tough-peas-wink.md new file mode 100644 index 0000000..8873923 --- /dev/null +++ b/.changeset/tough-peas-wink.md @@ -0,0 +1,5 @@ +--- +"@kopflos-cms/core": patch +--- + +Added support for `code:EcmaScriptTemplateLiteral` diff --git a/.changeset/wet-shirts-watch.md b/.changeset/wet-shirts-watch.md new file mode 100644 index 0000000..4947a3e --- /dev/null +++ b/.changeset/wet-shirts-watch.md @@ -0,0 +1,5 @@ +--- +"@kopflos-cms/logger": minor +--- + +Extracted package from `@kopflos-cms/core` diff --git a/.changeset/wild-cooks-leave.md b/.changeset/wild-cooks-leave.md new file mode 100644 index 0000000..a9f9e69 --- /dev/null +++ b/.changeset/wild-cooks-leave.md @@ -0,0 +1,5 @@ +--- +"kopflos": patch +--- + +Added `--mode (development|production)` option diff --git a/.eslintrc.json b/.eslintrc.json index a9ef0c8..9de5f61 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -5,5 +5,8 @@ }, "env": { "mocha": true - } + }, + "ignorePatterns": [ + "packages/sparql-path-parser/" + ] } diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6c0a8e8..8b5cd29 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -6,19 +6,38 @@ jobs: unit-tests: runs-on: [ ubuntu-latest ] strategy: + fail-fast: false matrix: node: [ 18, 20, "lts/*" ] + package: + - name: "@kopflos-cms/core" + path: packages/core + - name: "@kopflos-cms/express" + path: packages/express + - name: "@kopflos-cms/plugin-deploy-resources" + path: packages/plugin-deploy-resources + - name: "@kopflos-cms/serve-file" + path: packages/serve-file + - name: "@kopflos-cms/vite" + path: packages/vite + - name: "@kopflos-labs/html-template" + path: labs/html-template + - name: "@kopflos-labs/handlebars" + path: labs/handlebars + - name: sparql-path-parser + path: packages/sparql-path-parser steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node }} - run: npm ci - - run: npx c8 --all --reporter lcovonly npm run --ws --if-present test + - run: npx c8 --all --src ${{ matrix.package.path }} --reporter lcovonly --reporter text npm run -w ${{ matrix.package.path }} test - name: Codecov uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} + flags: ${{ matrix.package.name }} lint: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 6b0b6b4..4ae1722 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ coverage node_modules *.d.ts *.js +*.js.map diff --git a/codecov.yml b/codecov.yml index 17c6fb4..1af0608 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,2 +1,3 @@ ignore: - - packages/mocha-chai-rdf + - example + - packages/sparql-path-parser/src/grammar diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..1564ce8 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,2 @@ +oxigraph/ +dist/ diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..e9d73b9 --- /dev/null +++ b/example/README.md @@ -0,0 +1,35 @@ +# Kopflos example + +API inspired by [Read the Plaque](https://readtheplaque.com/). + +## Start + +1. Start the database: + ```bash + docker compose up -d + ``` +2. Start the server in development mode: + ```bash + npx kopflos serve --mode development + ``` + +The API is running on http://localhost:1429 + +## Details + +When starting, `kopflos` automatically seeds the database wit resource from directories `resources` and +`resources.dev` (configured in `kopflos.config.ts`). + +## Building for production + +First run the build command which uses vite to transform and bundle HTML templates. + +```bash +npx kopflos build +``` + +Then start the server in production mode: + +```bash +npx kopflos serve +``` diff --git a/example/docker-compose.yaml b/example/docker-compose.yaml new file mode 100644 index 0000000..4aa8bba --- /dev/null +++ b/example/docker-compose.yaml @@ -0,0 +1,10 @@ +version: '2' +services: + oxigraph: + image: ghcr.io/oxigraph/oxigraph:0.4.1 + user: root + command: serve --location /data --bind 0.0.0.0:7878 + ports: + - 7878:7878 + volumes: + - ./oxigraph:/data diff --git a/example/kopflos.config.ts b/example/kopflos.config.ts new file mode 100644 index 0000000..160da5d --- /dev/null +++ b/example/kopflos.config.ts @@ -0,0 +1,32 @@ +import * as url from 'node:url' +import type { KopflosConfig } from '@kopflos-cms/core' + +export default { + baseIri: 'http://localhost:1429', + apiGraphs: ['http://localhost:1429/api'], + sparql: { + default: { + endpointUrl: 'http://localhost:7878/query?union-default-graph', + updateUrl: 'http://localhost:7878/update', + }, + }, + variables: { + uiRoot: 'ui', + }, + plugins: { + '@kopflos-cms/plugin-deploy-resources': { + paths: ['resources', 'resources.dev'], + }, + '@kopflos-cms/express/middleware': { + before: [ + 'cors', + ['compression', { level: 9 }], + url.fileURLToPath(new URL('lib/static.js', import.meta.url)), + ], + }, + '@kopflos-cms/vite': { + root: 'ui', + entrypoints: ['ui/*.html'], + }, + }, +} diff --git a/example/lib/static.ts b/example/lib/static.ts new file mode 100644 index 0000000..5a9c0a7 --- /dev/null +++ b/example/lib/static.ts @@ -0,0 +1,5 @@ +import express from 'express' + +export default (): express.RequestHandler => { + return express.static('public') +} diff --git a/example/lib/templateData.ts b/example/lib/templateData.ts new file mode 100644 index 0000000..f898c15 --- /dev/null +++ b/example/lib/templateData.ts @@ -0,0 +1,7 @@ +import type { TemplateDataFunc } from '@kopflos-labs/html-template' + +export const describe = (resourcePath: string): TemplateDataFunc => ({ env }) => { + return env.sparql.default.stream.query.construct(` +BASE <${env.kopflos.config.baseIri}> +DESCRIBE <${resourcePath}>`) +} diff --git a/example/package.json b/example/package.json new file mode 100644 index 0000000..14e2d76 --- /dev/null +++ b/example/package.json @@ -0,0 +1,30 @@ +{ + "name": "example", + "private": true, + "type": "module", + "scripts": { + "build": "kopflos build", + "start": "kopflos serve --mode development --trust-proxy", + "prestart:prod": "npm run build", + "start:prod": "kopflos serve --trust-proxy --variable uiRoot=dist" + }, + "dependencies": { + "@kopflos-cms/vite": "*", + "@kopflos-cms/serve-file": "*", + "@kopflos-labs/html-template": "*", + "@kopflos-labs/handlebars": "*", + "@kopflos-labs/lit": "*", + "@openlayers-elements/core": "^0.3.0", + "@openlayers-elements/maps": "^0.3.0", + "@shoelace-style/shoelace": "^2.17.1", + "@webcomponents/template-shadowroot": "^0.2.1", + "express": "^5.0.1", + "kopflos": "*", + "lit-element": "^4.1.1", + "cors": "^2.8.5", + "compression": "^1.7.4" + }, + "devDependencies": { + "@types/express": "^5" + } +} diff --git a/example/public/foo.txt b/example/public/foo.txt new file mode 100644 index 0000000..5716ca5 --- /dev/null +++ b/example/public/foo.txt @@ -0,0 +1 @@ +bar diff --git a/example/resources.dev/plaque/newton-s-apple-tree-monash-university.ttl b/example/resources.dev/plaque/newton-s-apple-tree-monash-university.ttl new file mode 100644 index 0000000..aec01ba --- /dev/null +++ b/example/resources.dev/plaque/newton-s-apple-tree-monash-university.ttl @@ -0,0 +1,35 @@ +PREFIX schema: + +<> + a ; + schema:identifier "newton-s-apple-tree-monash-university" ; + schema:name "Newton's Apple Tree Monash University" ; + schema:image + [ + schema:url + + ] ; + schema:contributor + [ + schema:name "@wadekelly" ; + schema:url ; + ] ; + schema:geo [ + schema:latitude "-37.909878" ; + schema:longitude "145.132809" ; + ] ; + schema:text """ +NEWTON'S APPLE +Newton's Apple tree, +the Flower of Kent variety, +was planted in 1975 and is +a cutting from the original tree +from which Sir Isaac Newton +reportedly observed an +apple fall, prompting him to +formulate his theories of +motion and gravity, one of the +most remarkable insights in +human history. +"""; +. diff --git a/example/resources/api/index.ttl b/example/resources/api/index.ttl new file mode 100644 index 0000000..d95f085 --- /dev/null +++ b/example/resources/api/index.ttl @@ -0,0 +1,71 @@ +PREFIX arg: +PREFIX code: +PREFIX dash: +PREFIX rdfs: +PREFIX schema: +PREFIX sh: +PREFIX kl: + +<> + a kl:Api ; + kl:resourceLoader kl:OwnGraphLoader ; +. + +<#plaque> + a kl:ResourceShape ; + kl:api <> ; + sh:targetClass ; +. + +<#plaque.html> + a kl:ResourceShape ; + kl:api <> ; + sh:target + [ + a kl:PatternedTarget ; + kl:regex "(?/(?[^/]+)/(?.+))\\.html$" ; + # TODO: support URI Templates + # kl:uriTemplate "/{type}{/identifier+}.html$" ; + ] ; + kl:handler + [ + a kl:Handler ; + kl:method "GET" ; + code:implementedBy + ( + [ + a code:EcmaScriptModule ; + code:link ; + code:arguments ( "${uiRoot}/${type}.html"^^code:EcmaScriptTemplateLiteral ) ; + ] + [ + a code:EcmaScriptModule ; + code:link ; + ] + [ + a code:EcmaScriptModule ; + code:link ; + code:arguments + ( + [ + a code:EcmaScriptModule ; + code:link + ] + [ + a code:EcmaScriptModule ; + code:link ; + ] + "${uri}"^^code:EcmaScriptTemplateLiteral + ) ; + ] + [ + a code:EcmaScriptModule ; + code:link ; + code:arguments + ( + [ code:link ; a code:EcmaScriptModule ] + ) + ] + ) + ] ; +. diff --git a/example/resources/api/schema.ttl b/example/resources/api/schema.ttl new file mode 100644 index 0000000..794854c --- /dev/null +++ b/example/resources/api/schema.ttl @@ -0,0 +1,5 @@ +PREFIX rdfs: + + + a rdfs:Class ; +. diff --git a/example/ui/component/my-header.ts b/example/ui/component/my-header.ts new file mode 100644 index 0000000..04ee698 --- /dev/null +++ b/example/ui/component/my-header.ts @@ -0,0 +1,20 @@ +import { LitElement, html, css } from 'lit-element' +import { customElement, property } from 'lit-element/decorators.js' + +@customElement('my-header') +export class MyHeader extends LitElement { + public static get styles() { + return css` + h1 { + color: red + } + ` + } + + @property({ type: String }) + public header!: string + + render() { + return html`

${this.header}

` + } +} diff --git a/example/ui/lib/maps.ts b/example/ui/lib/maps.ts new file mode 100644 index 0000000..dd31f38 --- /dev/null +++ b/example/ui/lib/maps.ts @@ -0,0 +1,4 @@ +import '@openlayers-elements/core/ol-map.js' +import '@openlayers-elements/maps/ol-marker-icon.js' +import '@openlayers-elements/core/ol-layer-vector.js' +import '@openlayers-elements/maps/ol-layer-openstreetmap.js' diff --git a/example/ui/lib/shoelace.ts b/example/ui/lib/shoelace.ts new file mode 100644 index 0000000..8fe09e2 --- /dev/null +++ b/example/ui/lib/shoelace.ts @@ -0,0 +1,2 @@ +import '@shoelace-style/shoelace/dist/components/button/button.js' +import '@shoelace-style/shoelace/dist/components/card/card.js' diff --git a/example/ui/plaque.html b/example/ui/plaque.html new file mode 100644 index 0000000..ee00a0a --- /dev/null +++ b/example/ui/plaque.html @@ -0,0 +1,100 @@ + + + + + + + + + + + + + +
+

© 2024 Zazuko

+
+ + + + diff --git a/example/ui/scripts.ts b/example/ui/scripts.ts new file mode 100644 index 0000000..71a12dd --- /dev/null +++ b/example/ui/scripts.ts @@ -0,0 +1,4 @@ +import '@kopflos-labs/lit/hydrate-support.js' +import './lib/shoelace.js' +import './component/my-header.js' +import('./lib/maps.js') diff --git a/example/ui/shadowRoot.ts b/example/ui/shadowRoot.ts new file mode 100644 index 0000000..5609876 --- /dev/null +++ b/example/ui/shadowRoot.ts @@ -0,0 +1,15 @@ +// Check if we require the template shadow root polyfill. +(async () => { + if (!HTMLTemplateElement.prototype.hasOwnProperty('shadowRoot')) { // eslint-disable-line no-prototype-builtins + // Fetch the template shadow root polyfill. + const { hydrateShadowRoots } = await import('@webcomponents/template-shadowroot/template-shadowroot.js') + + // Apply the polyfill. This is a one-shot operation, so it is important + // it happens after all HTML has been parsed. + hydrateShadowRoots(document.body) + + // At this point, browsers without native declarative shadow DOM + // support can paint the initial state of your components! + document.body.removeAttribute('dsd-pending') + } +})() diff --git a/labs/handlebars/README.md b/labs/handlebars/README.md new file mode 100644 index 0000000..1569786 --- /dev/null +++ b/labs/handlebars/README.md @@ -0,0 +1,61 @@ +# Handlebars templates in kopflos + +Processes handlebars templates in kopflos. Used together with the [@kopflos-labs/html-template](https://npm.im/@kopflos-labs/html-template). + +## Setup + +Add to a handler chain to process handlebars templates. + +```turtle +[ + a kl:ResourceShape ; + kl:handler + [ + a kl:Handler ; + kl:method "GET" ; + code:implementedBy + ( + # ...previous handlers to prepare the template... + [ + a code:EcmaScriptModule ; + code:link ; + code:arguments + ( + [ a code:EcmaScriptModule; code:link ] + "${uri}"^^code:EcmaScriptTemplateLiteral + ) ; + ] + ) + ] ; +] . +``` + +See [@kopflos-labs/html-template](https://npm.im/@kopflos-labs/html-template) for more general information +on writing templates. + +## Template helpers + +### `valueof` + +Requires exactly one argument which is a named node or SHACL Property Path. + +As a named node, it can be a prefixed name known to the `@zazuko/prefixes` package. + +Presently, only Sequence Paths are supported. + +```html + + + + + + + + + +``` diff --git a/labs/handlebars/index.ts b/labs/handlebars/index.ts new file mode 100644 index 0000000..22703c2 --- /dev/null +++ b/labs/handlebars/index.ts @@ -0,0 +1,17 @@ +import hbs from 'handlebars' +import type { TemplateFunc } from '@kopflos-labs/html-template' +import { valueof } from './lib/helpers.js' + +const processTemplate: TemplateFunc = function (template, context, env): string { + const compiled = hbs.compile(template) + return compiled(context, { + helpers: { + valueof: valueof(env.kopflos.config.baseIri), + }, + allowedProtoProperties: { + value: true, + }, + }) +} + +export default processTemplate diff --git a/labs/handlebars/lib/helpers.ts b/labs/handlebars/lib/helpers.ts new file mode 100644 index 0000000..9cca6ce --- /dev/null +++ b/labs/handlebars/lib/helpers.ts @@ -0,0 +1,22 @@ +import type { ShaclPropertyPath } from 'clownface-shacl-path' +import { findNodes } from 'clownface-shacl-path' +import { parse } from 'sparql-path-parser' +import type { TemplateContext } from '@kopflos-labs/html-template' + +export const valueof = (baseIRI: string) => { + const cache = new Map() + + return function (this: TemplateContext, property: string = '') { + let propertyPath = cache.get(property) + try { + if (!propertyPath) { + propertyPath = parse(property, { baseIRI }) + cache.set(property, propertyPath) + } + } catch (error: unknown) { + return (error as Error).message + } + + return findNodes(this.pointer, propertyPath).value + } +} diff --git a/labs/handlebars/package.json b/labs/handlebars/package.json new file mode 100644 index 0000000..bc034fd --- /dev/null +++ b/labs/handlebars/package.json @@ -0,0 +1,50 @@ +{ + "name": "@kopflos-labs/handlebars", + "version": "0.0.0", + "type": "module", + "main": "index.js", + "scripts": { + "test": "mocha", + "build": "tsc", + "prepack": "npm run build" + }, + "exports": { + ".": "./index.js" + }, + "dependencies": { + "@zazuko/env": "^2.3.0", + "@zazuko/prefixes": "^2.2.0", + "clownface-shacl-path": "^2.4.0", + "handlebars": "^4.7.8", + "sparql-path-parser": "^0.0.0" + }, + "devDependencies": { + "chai": "^5.1.1", + "mocha-chai-rdf": "^0.1.4" + }, + "files": [ + "*.js", + "*.d.ts", + "lib/*.js", + "lib/*.d.ts" + ], + "author": "Zazuko GmbH", + "license": "MIT", + "repository": { + "type": "git", + "url": "git://github.com/zazuko/kopflos.git", + "directory": "labs/handlebars" + }, + "bugs": { + "url": "https://github.com/zazuko/kopflos/issues" + }, + "homepage": "https://github.com/zazuko/kopflos", + "mocha": { + "extension": [ + "ts" + ], + "spec": "test/**/*.test.ts", + "loader": "ts-node/esm", + "require": "../../mocha-setup.js" + } +} diff --git a/labs/handlebars/test/index.test.ts b/labs/handlebars/test/index.test.ts new file mode 100644 index 0000000..f3192b6 --- /dev/null +++ b/labs/handlebars/test/index.test.ts @@ -0,0 +1,43 @@ +import { createStore } from 'mocha-chai-rdf/store.js' +import { expect } from 'chai' +import { createEnv } from '@kopflos-cms/core/env.js' // eslint-disable-line import/no-unresolved +import process from '../index.js' + +describe('@kopflos-labs/handlebars', () => { + beforeEach(createStore(import.meta.url, { + format: 'ttl', + })) + + const env = createEnv({ + baseIri: 'http://example.org/', + sparql: { + default: 'http://example.org/sparql', + }, + }) + + it("allows to use 'value' property of pointer", function () { + const result = process('{{ pointer.value }}', { + pointer: this.rdf.graph.namedNode('http://example.org/article'), + }, env) + + expect(result).to.equal('http://example.org/article') + }) + + describe('valueof helper', () => { + it('follows the path and prints value', function () { + const result = process('{{ valueof "schema:image/schema:thumbnail/schema:contentUrl" }}', { + pointer: this.rdf.graph.namedNode('http://example.org/article'), + }, env) + + expect(result).to.equal('http://example.org/thumbnail.jpg') + }) + + it('prints parse error', function () { + const result = process('{{{ valueof "schemaimage" }}}', { + pointer: this.rdf.graph.namedNode('http://example.org/article'), + }, env) + + expect(result).to.match(/mismatched input 'schemaimage'/) + }) + }) +}) diff --git a/labs/handlebars/test/index.test.ts.ttl b/labs/handlebars/test/index.test.ts.ttl new file mode 100644 index 0000000..8d78c47 --- /dev/null +++ b/labs/handlebars/test/index.test.ts.ttl @@ -0,0 +1,12 @@ +PREFIX schema: +PREFIX ex: + +ex:article + schema:image + [ + schema:thumbnail + [ + schema:contentUrl + ] ; + ] ; +. diff --git a/labs/html-template/README.md b/labs/html-template/README.md new file mode 100644 index 0000000..a6f4e8f --- /dev/null +++ b/labs/html-template/README.md @@ -0,0 +1,85 @@ +# Template processor for kopflos + +Renders `