mirror of
https://github.com/modernweb-dev/rocket.git
synced 2026-03-21 15:54:57 +00:00
Compare commits
5 Commits
@rocket/en
...
@rocket/en
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff8b4c5cd5 | ||
|
|
5122ea8639 | ||
|
|
3032ba9b82 | ||
|
|
93503ed309 | ||
|
|
77646abbee |
@@ -28,7 +28,7 @@
|
||||
<p align="center">
|
||||
<a href="https://rocket.modern-web.dev">Website</a>
|
||||
·
|
||||
<a href="https://rocket.modern-web.dev/doc/">Documentation</a>
|
||||
<a href="https://rocket.modern-web.dev/docs/">Documentation</a>
|
||||
·
|
||||
<a href="https://rocket.modern-web.dev/chat">Discord Community</a>
|
||||
</p>
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
# @rocket/engine
|
||||
|
||||
## 0.2.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 93503ed: HTML in headings will be ignored for the menu
|
||||
Some examples:
|
||||
|
||||
- `<h1>Hello <em>Word</em></h1>` => `Hello Word`
|
||||
- `<h1>Hello <strong>World</strong> of <span>JS <em>(JavaScript)</em></span>!</h1>` => `Hello World of JS (JavaScript)!`
|
||||
|
||||
- 3032ba9: Menus now support special characters in markdown headings.
|
||||
|
||||
Examples:
|
||||
|
||||
```md
|
||||
# Fun Errors & Feedback
|
||||
|
||||
# <some-button>
|
||||
```
|
||||
|
||||
## 0.2.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@rocket/engine",
|
||||
"version": "0.2.4",
|
||||
"version": "0.2.5",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
|
||||
@@ -29,9 +29,23 @@ export function getHtmlMetaData(htmlFilePath) {
|
||||
// headlinesWithId: [],
|
||||
};
|
||||
|
||||
/** @type {string | undefined} */
|
||||
let capturedHeadlineText = undefined;
|
||||
parser.eventHandler = (ev, _data) => {
|
||||
if (ev === SaxEventType.OpenTag) {
|
||||
const data = /** @type {Tag} */ (/** @type {any} */ (_data));
|
||||
if (isHeadline(data)) {
|
||||
capturedHeadlineText = '';
|
||||
}
|
||||
}
|
||||
if (capturedHeadlineText !== undefined && ev === SaxEventType.Text) {
|
||||
const data = /** @type {Text} */ (/** @type {any} */ (_data));
|
||||
capturedHeadlineText += data.value;
|
||||
}
|
||||
|
||||
if (ev === SaxEventType.CloseTag) {
|
||||
const data = /** @type {Tag} */ (/** @type {any} */ (_data));
|
||||
// ********** <meta name="*" content="*">
|
||||
if (data.name === 'meta') {
|
||||
const metaName = getAttribute(data, 'name');
|
||||
if (metaName === 'menu:link.text') {
|
||||
@@ -63,26 +77,32 @@ export function getHtmlMetaData(htmlFilePath) {
|
||||
if (!metaData.title && data.name === 'title') {
|
||||
metaData.title = getText(data);
|
||||
}
|
||||
if (!metaData.h1 && data.name === 'h1') {
|
||||
metaData.h1 = getText(data);
|
||||
}
|
||||
|
||||
// ********** <h1> - <h6>
|
||||
if (isHeadline(data)) {
|
||||
const id = getAttribute(data, 'id');
|
||||
const rawText = getText(data);
|
||||
const linkText = getAttribute(data, 'link-text');
|
||||
if (id && rawText) {
|
||||
const processedCapturedHeadlineText = capturedHeadlineText
|
||||
?.replace(/</g, '<')
|
||||
.replace(/&/g, '&')
|
||||
.trim();
|
||||
const text = linkText || processedCapturedHeadlineText || '';
|
||||
if (data.name === 'h1') {
|
||||
metaData.h1 = text;
|
||||
}
|
||||
if (id && text) {
|
||||
if (!metaData.headlinesWithId) {
|
||||
metaData.headlinesWithId = [];
|
||||
}
|
||||
const rawTextObj = linkText ? { rawText } : {};
|
||||
const rawTextObj = linkText ? { rawText: processedCapturedHeadlineText } : {};
|
||||
metaData.headlinesWithId.push({
|
||||
text: linkText || rawText,
|
||||
text,
|
||||
id,
|
||||
level: parseInt(data.name[1], 10),
|
||||
...rawTextObj,
|
||||
});
|
||||
}
|
||||
capturedHeadlineText = undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -97,6 +117,7 @@ export function getHtmlMetaData(htmlFilePath) {
|
||||
});
|
||||
readable.on('end', () => {
|
||||
parser.end();
|
||||
capturedHeadlineText = undefined;
|
||||
|
||||
resolve(metaData);
|
||||
});
|
||||
|
||||
@@ -9,6 +9,9 @@ const require = createRequire(import.meta.url);
|
||||
export const streamOptions = { highWaterMark: 128 * 1024 };
|
||||
const saxPath = require.resolve('sax-wasm/lib/sax-wasm.wasm');
|
||||
const saxWasmBuffer = await readFile(saxPath);
|
||||
export const parser = new SAXParser(SaxEventType.CloseTag, streamOptions);
|
||||
export const parser = new SAXParser(
|
||||
SaxEventType.OpenTag | SaxEventType.CloseTag | SaxEventType.Text,
|
||||
streamOptions,
|
||||
);
|
||||
|
||||
await parser.prepareWasm(saxWasmBuffer);
|
||||
|
||||
@@ -834,4 +834,99 @@ describe('Engine menus', () => {
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
it('14: get-all-text-but-strip-html', async () => {
|
||||
const { build, readSource } = await setupTestEngine(
|
||||
'fixtures/05-menu/14-get-all-text-but-strip-html/docs',
|
||||
);
|
||||
await build();
|
||||
|
||||
expect(JSON.parse(readSource('pageTreeData.rocketGenerated.json'))).to.deep.equal({
|
||||
h1: 'Hello World of JS (JavaScript)!',
|
||||
name: 'Hello World of JS (JavaScript)!',
|
||||
menuLinkText: 'Hello World of JS (JavaScript)!',
|
||||
url: '/',
|
||||
outputRelativeFilePath: 'index.html',
|
||||
sourceRelativeFilePath: 'index.rocket.js',
|
||||
level: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('15: markdown special characters', async () => {
|
||||
const { build, readSource } = await setupTestEngine(
|
||||
'fixtures/05-menu/15-md-special-characters/docs',
|
||||
);
|
||||
await build();
|
||||
|
||||
expect(JSON.parse(readSource('pageTreeData.rocketGenerated.json'))).to.deep.equal({
|
||||
children: [
|
||||
{
|
||||
h1: '<some-button>',
|
||||
headlinesWithId: [
|
||||
{
|
||||
id: 'some-button',
|
||||
level: 1,
|
||||
text: '<some-button>',
|
||||
},
|
||||
],
|
||||
level: 1,
|
||||
menuLinkText: '<some-button>',
|
||||
name: '<some-button>',
|
||||
outputRelativeFilePath: 'component/index.html',
|
||||
sourceRelativeFilePath: 'component.rocket.md',
|
||||
url: '/component/',
|
||||
},
|
||||
],
|
||||
h1: 'Fun Errors & Feedback',
|
||||
headlinesWithId: [
|
||||
{
|
||||
id: 'fun-errors--feedback',
|
||||
level: 1,
|
||||
text: 'Fun Errors & Feedback',
|
||||
},
|
||||
],
|
||||
level: 0,
|
||||
menuLinkText: 'Fun Errors & Feedback',
|
||||
name: 'Fun Errors & Feedback',
|
||||
outputRelativeFilePath: 'index.html',
|
||||
sourceRelativeFilePath: 'index.rocket.md',
|
||||
url: '/',
|
||||
});
|
||||
});
|
||||
|
||||
it('15: link-text attribute', async () => {
|
||||
const { build, readSource } = await setupTestEngine(
|
||||
'fixtures/05-menu/16-link-text-attribute/docs',
|
||||
);
|
||||
await build();
|
||||
|
||||
expect(JSON.parse(readSource('pageTreeData.rocketGenerated.json'))).to.deep.equal({
|
||||
h1: 'Home',
|
||||
headlinesWithId: [
|
||||
{
|
||||
id: 'home',
|
||||
level: 1,
|
||||
rawText: 'Welcome to Rocket',
|
||||
text: 'Home',
|
||||
},
|
||||
{
|
||||
id: 'first',
|
||||
level: 2,
|
||||
text: 'First',
|
||||
},
|
||||
{
|
||||
id: 'second',
|
||||
level: 2,
|
||||
rawText: 'Second is best',
|
||||
text: 'Second',
|
||||
},
|
||||
],
|
||||
level: 0,
|
||||
menuLinkText: 'Home',
|
||||
name: 'Home',
|
||||
outputRelativeFilePath: 'index.html',
|
||||
sourceRelativeFilePath: 'index.rocket.js',
|
||||
url: '/',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -116,10 +116,21 @@ describe('Engine start error handling', () => {
|
||||
});
|
||||
|
||||
it('04: update-header-while-rendering', async () => {
|
||||
const { readOutput, writeSource, cleanup, engine, setAsOpenedInBrowser, outputExists } =
|
||||
await setupTestEngine(
|
||||
const {
|
||||
readOutput,
|
||||
writeSource,
|
||||
cleanup,
|
||||
engine,
|
||||
setAsOpenedInBrowser,
|
||||
outputExists,
|
||||
anEngineEvent,
|
||||
} = await setupTestEngine(
|
||||
'fixtures/09b-watch-error-handling/04-update-header-while-rendering/docs',
|
||||
);
|
||||
expect(outputExists('index.html')).to.be.false;
|
||||
|
||||
await engine.start();
|
||||
setAsOpenedInBrowser('index.rocket.js');
|
||||
await writeSource(
|
||||
'index.rocket.js',
|
||||
[
|
||||
@@ -140,13 +151,7 @@ describe('Engine start error handling', () => {
|
||||
'`;',
|
||||
].join('\n'),
|
||||
);
|
||||
|
||||
await engine.start();
|
||||
setAsOpenedInBrowser('index.rocket.js');
|
||||
|
||||
const { port } = engine.devServer.config;
|
||||
expect(outputExists('index.html')).to.be.false;
|
||||
await fetch(`http://localhost:${port}/`);
|
||||
await anEngineEvent('rocketUpdated');
|
||||
|
||||
expect(readOutput('index.html')).to.equal(
|
||||
[
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"title": "Fixed Title",
|
||||
"h1": "\n Welcome Members:\n ",
|
||||
"h1": "Welcome Members:",
|
||||
"headlinesWithId": [
|
||||
{
|
||||
"text": "\n Welcome Members:\n ",
|
||||
"text": "Welcome Members:",
|
||||
"id": "welcome-members",
|
||||
"level": 1
|
||||
}
|
||||
],
|
||||
"name": "Fixed Title",
|
||||
"menuLinkText": "\n Welcome Members:\n ",
|
||||
"menuLinkText": "Welcome Members:",
|
||||
"url": "/",
|
||||
"outputRelativeFilePath": "index.html",
|
||||
"sourceRelativeFilePath": "index.rocket.js",
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/* START - Rocket auto generated - do not touch */
|
||||
export const sourceRelativeFilePath = 'index.rocket.js';
|
||||
import { layout, html } from './recursive.data.js';
|
||||
export { layout, html };
|
||||
/* END - Rocket auto generated - do not touch */
|
||||
|
||||
export default () =>
|
||||
html`<h1>
|
||||
Hello <strong>World</strong> of <span>JS <em>(JavaScript)</em></span
|
||||
>!
|
||||
</h1>`;
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"h1": "Hello World of JS (JavaScript)!",
|
||||
"name": "Hello World of JS (JavaScript)!",
|
||||
"menuLinkText": "Hello World of JS (JavaScript)!",
|
||||
"url": "/",
|
||||
"outputRelativeFilePath": "index.html",
|
||||
"sourceRelativeFilePath": "index.rocket.js",
|
||||
"level": 0
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { PageTree, SiteMenu } from '@rocket/engine';
|
||||
import { html } from 'lit';
|
||||
|
||||
const pageTree = new PageTree({
|
||||
inputDir: new URL('./', import.meta.url),
|
||||
outputDir: new URL('../__output', import.meta.url),
|
||||
});
|
||||
|
||||
await pageTree.restore();
|
||||
|
||||
export const layout = data => {
|
||||
return html`
|
||||
${pageTree.renderMenu(new SiteMenu(), data.sourceRelativeFilePath)}
|
||||
<main>${data.content()}</main>
|
||||
`;
|
||||
};
|
||||
|
||||
export { html };
|
||||
@@ -0,0 +1,9 @@
|
||||
```js server
|
||||
/* START - Rocket auto generated - do not touch */
|
||||
export const sourceRelativeFilePath = 'component.rocket.md';
|
||||
import { layout, html } from './recursive.data.js';
|
||||
export { layout, html };
|
||||
/* END - Rocket auto generated - do not touch */
|
||||
```
|
||||
|
||||
# <some-button>
|
||||
@@ -0,0 +1,9 @@
|
||||
```js server
|
||||
/* START - Rocket auto generated - do not touch */
|
||||
export const sourceRelativeFilePath = 'index.rocket.md';
|
||||
import { layout, html } from './recursive.data.js';
|
||||
export { layout, html };
|
||||
/* END - Rocket auto generated - do not touch */
|
||||
```
|
||||
|
||||
# Fun Errors & Feedback
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"h1": "Fun Errors & Feedback",
|
||||
"headlinesWithId": [
|
||||
{
|
||||
"text": "Fun Errors & Feedback",
|
||||
"id": "fun-errors--feedback",
|
||||
"level": 1
|
||||
}
|
||||
],
|
||||
"name": "Fun Errors & Feedback",
|
||||
"menuLinkText": "Fun Errors & Feedback",
|
||||
"url": "/",
|
||||
"outputRelativeFilePath": "index.html",
|
||||
"sourceRelativeFilePath": "index.rocket.md",
|
||||
"level": 0,
|
||||
"children": [
|
||||
{
|
||||
"h1": "<some-button>",
|
||||
"headlinesWithId": [
|
||||
{
|
||||
"text": "<some-button>",
|
||||
"id": "some-button",
|
||||
"level": 1
|
||||
}
|
||||
],
|
||||
"name": "<some-button>",
|
||||
"menuLinkText": "<some-button>",
|
||||
"url": "/component/",
|
||||
"outputRelativeFilePath": "component/index.html",
|
||||
"sourceRelativeFilePath": "component.rocket.md",
|
||||
"level": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { PageTree, SiteMenu } from '@rocket/engine';
|
||||
import { html } from 'lit';
|
||||
|
||||
const pageTree = new PageTree({
|
||||
inputDir: new URL('./', import.meta.url),
|
||||
outputDir: new URL('../__output', import.meta.url),
|
||||
});
|
||||
|
||||
await pageTree.restore();
|
||||
|
||||
export const layout = data => {
|
||||
return html`
|
||||
${pageTree.renderMenu(new SiteMenu(), data.sourceRelativeFilePath)}
|
||||
<main>${data.content()}</main>
|
||||
`;
|
||||
};
|
||||
|
||||
export { html };
|
||||
@@ -0,0 +1,11 @@
|
||||
/* START - Rocket auto generated - do not touch */
|
||||
export const sourceRelativeFilePath = 'index.rocket.js';
|
||||
import { layout, html } from './recursive.data.js';
|
||||
export { layout, html };
|
||||
/* END - Rocket auto generated - do not touch */
|
||||
|
||||
export default () => html`
|
||||
<h1 id="home" link-text="Home">Welcome to Rocket</h1>
|
||||
<h2 id="first">First</h2>
|
||||
<h2 link-text="Second" id="second">Second is best</h2>
|
||||
`;
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"h1": "Home",
|
||||
"headlinesWithId": [
|
||||
{
|
||||
"text": "Home",
|
||||
"id": "home",
|
||||
"level": 1,
|
||||
"rawText": "Welcome to Rocket"
|
||||
},
|
||||
{
|
||||
"text": "First",
|
||||
"id": "first",
|
||||
"level": 2
|
||||
},
|
||||
{
|
||||
"text": "Second",
|
||||
"id": "second",
|
||||
"level": 2,
|
||||
"rawText": "Second is best"
|
||||
}
|
||||
],
|
||||
"name": "Home",
|
||||
"menuLinkText": "Home",
|
||||
"url": "/",
|
||||
"outputRelativeFilePath": "index.html",
|
||||
"sourceRelativeFilePath": "index.rocket.js",
|
||||
"level": 0
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { PageTree, SiteMenu } from '@rocket/engine';
|
||||
import { html } from 'lit';
|
||||
|
||||
const pageTree = new PageTree({
|
||||
inputDir: new URL('./', import.meta.url),
|
||||
outputDir: new URL('../__output', import.meta.url),
|
||||
});
|
||||
|
||||
await pageTree.restore();
|
||||
|
||||
export const layout = data => {
|
||||
return html`
|
||||
${pageTree.renderMenu(new SiteMenu(), data.sourceRelativeFilePath)}
|
||||
<main>${data.content()}</main>
|
||||
`;
|
||||
};
|
||||
|
||||
export { html };
|
||||
@@ -85,22 +85,22 @@ In most cases you probably will not need to do anything as it will take the text
|
||||
So if you have a page like this:
|
||||
|
||||
```md
|
||||
# Hello World
|
||||
# Learning Rocket
|
||||
```
|
||||
|
||||
then it will be called "Hello World" in the menu.
|
||||
then it will be called "Learning Rocket" in the menu.
|
||||
|
||||
You can overwrite that by using the property `menuLinkText`;
|
||||
|
||||
````md
|
||||
```js server
|
||||
export const menuLinkText = 'Hello';
|
||||
export const menuLinkText = 'Docs';
|
||||
```
|
||||
|
||||
# Hello World
|
||||
# Learning Rocket
|
||||
````
|
||||
|
||||
Now the menu will be called "Hello".
|
||||
Now the menu will be called "Docs".
|
||||
|
||||
Within a menu the text of the links is defined by the following priority:
|
||||
|
||||
@@ -111,6 +111,32 @@ Within a menu the text of the links is defined by the following priority:
|
||||
|
||||
You can influence that data that gets provided to the menu by setting exports.
|
||||
|
||||
### link-text="..."
|
||||
|
||||
If you want to rename the menu text you can use the attribute `link-text`.
|
||||
It works on your h1 for the page title as well as on your h2-h6 for a table of contents menu.
|
||||
|
||||
Examples:
|
||||
|
||||
```html
|
||||
<h1 link-text="Docs">Learning Rocket</h1>
|
||||
|
||||
<h2 link-text="Contact">Write us a message</h2>
|
||||
```
|
||||
|
||||
<inline-notification>
|
||||
You can use HTML within markdown too!
|
||||
</inline-notification>
|
||||
|
||||
## Headings with HTML
|
||||
|
||||
HTML in headings will be ignored for the menu
|
||||
|
||||
Some examples:
|
||||
|
||||
- `<h1>Hello <em>Word</em></h1>` => `Hello Word`
|
||||
- `<h1>Hello <strong>World</strong> of <span>JS <em>(JavaScript)</em></span>!</h1>` => `Hello World of JS (JavaScript)!`
|
||||
|
||||
## Menu No Link
|
||||
|
||||
Often you have sections or groups of pages which you want to provide a heading for.
|
||||
|
||||
Reference in New Issue
Block a user