Compare commits

...

14 Commits

Author SHA1 Message Date
github-actions[bot]
ff8b4c5cd5 Version Packages 2022-08-15 23:04:55 +02:00
Thomas Allmer
5122ea8639 chore: add docs & tests for link-text for headlines 2022-08-15 23:02:29 +02:00
Thomas Allmer
3032ba9b82 feat(engine): menus now support special characters in markdown headings 2022-08-15 23:02:29 +02:00
Thomas Allmer
93503ed309 feat(engine): HTML in headings will be ignored for the menu 2022-08-15 23:02:29 +02:00
George Raptis
77646abbee Fix documentation link 2022-08-15 14:54:42 +02:00
github-actions[bot]
9ae3966fef Version Packages 2022-08-14 22:51:30 +02:00
Thomas Allmer
09a47b43dc fix(engine): prevent fatal error because of simultaneous write to file 2022-08-14 22:48:10 +02:00
Thomas Allmer
42d794bdfc chore: remove drawer pkg & fix contribution link in README.md 2022-08-13 21:40:50 +02:00
github-actions[bot]
b8a1b45953 Version Packages 2022-08-13 18:36:55 +02:00
Thomas Allmer
379f08ff47 feat(engine): remove global dom shim workaround 2022-08-13 18:31:20 +02:00
github-actions[bot]
50bb68ab7f Version Packages 2022-08-13 15:41:11 +02:00
Thomas Allmer
250ca87a9d chore: prepare for release of @mdjs/core v0.20.0 2022-08-13 15:36:47 +02:00
Thomas Allmer
6f88d8ef6f feat: use rehype-prism-plus; get rid of workaround for rehype-prism 2022-08-13 15:36:47 +02:00
jorenbroekema
35ed64dca1 feat(@mdjs/core): move to ESM 2022-08-13 15:36:47 +02:00
81 changed files with 1413 additions and 1043 deletions

View File

@@ -7,5 +7,6 @@
"search.exclude": { "search.exclude": {
"**/*-mdjs-generated.js": true, "**/*-mdjs-generated.js": true,
"**/dist-types": true, "**/dist-types": true,
} },
"editor.experimental.stickyScroll.enabled": true
} }

View File

@@ -28,7 +28,7 @@
<p align="center"> <p align="center">
<a href="https://rocket.modern-web.dev">Website</a> <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> <a href="https://rocket.modern-web.dev/chat">Discord Community</a>
</p> </p>
@@ -68,7 +68,7 @@ npx @rocket/create@latest
We are always looking for contributors of all skill levels! If you're looking to ease your way into the project, try out a [good first issue](https://github.com/modernweb-dev/rocket/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). We are always looking for contributors of all skill levels! If you're looking to ease your way into the project, try out a [good first issue](https://github.com/modernweb-dev/rocket/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).
If you are interested in helping contribute to Modern Web, please take a look at our [Contributing Guide](https://github.com/modernweb-dev/rocket/blob/next/CONTRIBUTING.md). Also, feel free to drop into [discord](https://rocket.modern-web.dev/chat) and say hi. 👋 If you are interested in helping contribute to Modern Web, please take a look at our [Contributing Guide](https://github.com/modernweb-dev/rocket/blob/main/CONTRIBUTING.md). Also, feel free to drop into [discord](https://rocket.modern-web.dev/chat) and say hi. 👋
### Financial Contributors ### Financial Contributors

View File

@@ -14,7 +14,7 @@
"@rocket/cli": "^0.20.0", "@rocket/cli": "^0.20.0",
"@rocket/engine": "^0.2.0", "@rocket/engine": "^0.2.0",
"@webcomponents/template-shadowroot": "^0.1.0", "@webcomponents/template-shadowroot": "^0.1.0",
"lit": "^2.2.5" "lit": "^2.3.0"
}, },
"@rocket/template-name": "Hydration Starter", "@rocket/template-name": "Hydration Starter",
"imports": { "imports": {

View File

@@ -18,7 +18,7 @@
"devDependencies": { "devDependencies": {
"@rocket/cli": "^0.20.0", "@rocket/cli": "^0.20.0",
"@rocket/engine": "^0.2.0", "@rocket/engine": "^0.2.0",
"lit": "^2.2.5" "lit": "^2.3.0"
}, },
"@rocket/template-name": "Blog Starter" "@rocket/template-name": "Blog Starter"
} }

View File

@@ -13,7 +13,7 @@
"devDependencies": { "devDependencies": {
"@rocket/cli": "^0.20.0", "@rocket/cli": "^0.20.0",
"@rocket/engine": "^0.2.0", "@rocket/engine": "^0.2.0",
"lit": "^2.2.5" "lit": "^2.3.0"
}, },
"@rocket/template-name": "Minimal Starter" "@rocket/template-name": "Minimal Starter"
} }

View File

@@ -18,7 +18,7 @@
"@sanity/client": "^3.1.0", "@sanity/client": "^3.1.0",
"@sanity/image-url": "^1.0.1", "@sanity/image-url": "^1.0.1",
"dotenv": "^16.0.0", "dotenv": "^16.0.0",
"lit": "^2.2.5" "lit": "^2.3.0"
}, },
"@rocket/template-name": "Sanity Minimal Starter" "@rocket/template-name": "Sanity Minimal Starter"
} }

View File

@@ -15,7 +15,7 @@
"@rocket/components": "^0.2.0", "@rocket/components": "^0.2.0",
"@rocket/engine": "^0.2.0", "@rocket/engine": "^0.2.0",
"@rocket/spark": "^0.2.0", "@rocket/spark": "^0.2.0",
"lit": "^2.2.5" "lit": "^2.3.0"
}, },
"@rocket/template-name": "Landing Page (@rocket/spark Theme)", "@rocket/template-name": "Landing Page (@rocket/spark Theme)",
"imports": { "imports": {

View File

@@ -15,7 +15,7 @@
"@rocket/engine": "^0.2.0", "@rocket/engine": "^0.2.0",
"@rocket/launch": "^0.21.0", "@rocket/launch": "^0.21.0",
"@rocket/search": "^0.7.0", "@rocket/search": "^0.7.0",
"lit": "^2.2.5" "lit": "^2.3.0"
}, },
"@rocket/template-name": "Documentation Website (@rocket/launch Theme)", "@rocket/template-name": "Documentation Website (@rocket/launch Theme)",
"imports": { "imports": {

View File

@@ -30,7 +30,7 @@
"rocket:build": "NODE_DEBUG=engine:rendering node --trace-warnings packages/cli/src/cli.js build", "rocket:build": "NODE_DEBUG=engine:rendering node --trace-warnings packages/cli/src/cli.js build",
"rocket:upgrade": "node packages/cli/src/cli.js upgrade", "rocket:upgrade": "node packages/cli/src/cli.js upgrade",
"search": "node packages/cli/src/cli.js search", "search": "node packages/cli/src/cli.js search",
"setup": "npm run setup:ts-configs && npm run setup:patches", "setup": "npm run setup:ts-configs",
"setup:patches": "npx patch-package", "setup:patches": "npx patch-package",
"setup:ts-configs": "node scripts/generate-ts-configs.mjs", "setup:ts-configs": "node scripts/generate-ts-configs.mjs",
"start:experimental": "NODE_DEBUG=engine:rendering node --no-warnings --experimental-loader ./packages/engine/src/litCssLoader.js packages/cli/src/cli.js start --open", "start:experimental": "NODE_DEBUG=engine:rendering node --no-warnings --experimental-loader ./packages/engine/src/litCssLoader.js packages/cli/src/cli.js start --open",

View File

@@ -38,7 +38,7 @@
"glob": "^7.0.0", "glob": "^7.0.0",
"minimatch": "^3.0.4", "minimatch": "^3.0.4",
"sax-wasm": "^2.0.0", "sax-wasm": "^2.0.0",
"slash": "^3.0.0" "slash": "^4.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/glob": "^7.0.0" "@types/glob": "^7.0.0"

View File

@@ -15,6 +15,9 @@
{ {
"path": "../plugins-manager/tsconfig.json" "path": "../plugins-manager/tsconfig.json"
}, },
{
"path": "../mdjs-core/tsconfig.json"
},
{ {
"path": "../engine/tsconfig.json" "path": "../engine/tsconfig.json"
} }

View File

@@ -43,7 +43,7 @@
"dependencies": { "dependencies": {
"@webcomponents/template-shadowroot": "^0.1.0", "@webcomponents/template-shadowroot": "^0.1.0",
"fontawesome-free": "^1.0.4", "fontawesome-free": "^1.0.4",
"lit": "^2.2.5" "lit": "^2.3.0"
}, },
"devDependencies": {}, "devDependencies": {},
"types": "./dist-types/exports/index.d.ts", "types": "./dist-types/exports/index.d.ts",

View File

@@ -1,37 +0,0 @@
# @rocket/drawer
## 0.1.5
### Patch Changes
- 1f14105: Add export map which enables side effect import via `@rocket/drawer/define`
## 0.1.4
### Patch Changes
- 445b028: Update to latest lit, @open-wc, @lion packages
## 0.1.3
### Patch Changes
- 0b64116: Update @lion dependencies
## 0.1.2
### Patch Changes
- 897892d: bump dependencies
## 0.1.1
### Patch Changes
- d955b43: reset translation on teardown overlay controller
## 0.1.0
### Minor Changes
- 1971f5d: Initial Release

View File

@@ -1,7 +0,0 @@
# Rocket Drawer
For mobile navigation on [Rocket sites](https://rocket.modern-web.dev/).
--
Inspired by [kenchris's menu-drawer](https://github.com/kenchris/websensor-compass/blob/master/scripts/menu-drawer.js).

View File

@@ -1 +0,0 @@
export { RocketDrawer } from './src/RocketDrawer.js';

View File

@@ -1,45 +0,0 @@
{
"name": "@rocket/drawer",
"version": "0.1.5",
"publishConfig": {
"access": "public"
},
"description": "Rocket stuff",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/modernweb-dev/rocket.git",
"directory": "packages/drawer"
},
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
"main": "index.js",
"exports": {
".": "./index.js",
"./rocket-drawer.js": "./rocket-drawer.js",
"./define": "./rocket-drawer.js"
},
"scripts": {
"dev": "web-dev-server --node-resolve --root-dir ../../ --open packages/drawer/ --watch",
"rocket:build": "node src/build/cli.js -c demo/docs",
"rocket:start": "node src/start/cli.js -c demo/docs --root-dir ../../ --open packages/cli/demo/docs/README.md",
"start": "npm run rocket:start",
"test": "mocha test-node/**/*.test.js test-node/*.test.js",
"test:watch": "mocha test-node/**/*.test.js test-node/*.test.js --watch"
},
"files": [
"*.js",
"dist-types",
"src"
],
"keywords": [
"storybook",
"demo",
"demo-states",
"testing"
],
"dependencies": {
"@lion/overlays": "^0.32.0",
"lit": "^2.0.0"
},
"types": "dist-types/index.d.ts"
}

View File

@@ -1,3 +0,0 @@
import { RocketDrawer } from './src/RocketDrawer.js';
customElements.define('rocket-drawer', RocketDrawer);

View File

@@ -1,264 +0,0 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { LitElement, html } from 'lit-element';
import { OverlayMixin, withModalDialogConfig } from '@lion/overlays';
/** @typedef {import('@lion/overlays/types/OverlayConfig').OverlayConfig} OverlayConfig */
/**
* @param {HTMLElement} el
*/
function transitionend(el) {
return new Promise(resolve => {
el.addEventListener('transitionend', resolve, { once: true });
});
}
// @ts-expect-error
export class RocketDrawer extends OverlayMixin(LitElement) {
static get properties() {
return {
useOverlay: { type: Boolean, reflect: true },
useOverlayMediaQuery: { type: String },
mediaMatcher: { type: Object },
};
}
// eslint-disable-next-line class-methods-use-this
// @ts-ignore
_defineOverlayConfig() {
return /** @type {OverlayConfig} */ {
...withModalDialogConfig(),
hidesOnOutsideClick: true,
viewportConfig: {
placement: 'slide',
},
};
}
_setupOverlayCtrl() {
if (this.useOverlay) {
super._setupOverlayCtrl();
/* eslint-disable no-param-reassign */
this._overlayCtrl.transitionHide = async ({ contentNode }) => {
contentNode.style.transition = 'transform 0.20s cubic-bezier(0.4, 0.0, 0.2, 1)';
contentNode.style.transform = 'translateX(-100%)';
await transitionend(contentNode);
// contentNode.style.display = 'none';
};
this._overlayCtrl.transitionShow = async ({ contentNode }) => {
contentNode.style.display = 'block';
contentNode.style.transform = 'translateX(-100%)';
contentNode.style.transition = 'transform 0.25s cubic-bezier(0.4, 0.0, 0.2, 1)';
// wait for display block to be "updated in the dom" and then translate otherwise there will be no animation
await new Promise(resolve => requestAnimationFrame(resolve));
await new Promise(resolve => requestAnimationFrame(resolve));
contentNode.style.transform = 'translateX(0)';
await transitionend(contentNode);
};
/* eslint-enable no-param-reassign */
this._overlayCtrl.contentNode.style.transform = 'translateX(-100%)';
this._overlayCtrl.contentNode.style.willChange = 'transform';
// gesture
this.containerEl = this._overlayCtrl.contentNode;
}
}
_teardownOverlayCtrl() {
super._teardownOverlayCtrl();
this._overlayCtrl.contentNode.style.transform = 'translateX(0)';
}
/** @param {import('lit-element').PropertyValues } changedProperties */
updated(changedProperties) {
super.updated(changedProperties);
if (changedProperties.has('opened')) {
if (this.opened) {
document.body.addEventListener('touchstart', this.onGestureStart, { passive: true });
} else {
document.body.removeEventListener('touchstart', this.onGestureStart);
}
}
if (changedProperties.has('useOverlay')) {
if (this.useOverlay) {
this._setupOverlayCtrl();
} else {
if (this._overlayCtrl) {
this._teardownOverlayCtrl();
}
}
}
if (changedProperties.has('useOverlayMediaQuery')) {
this.mediaMatcher.removeEventListener('change', this.onMatchMedia);
this.mediaMatcher = window.matchMedia(this.useOverlayMediaQuery);
this.mediaMatcher.addEventListener('change', this.onMatchMedia);
this.useOverlay = !!this.mediaMatcher.matches;
}
}
/**
* @param { MediaQueryListEvent } query
*/
onMatchMedia(query) {
this.useOverlay = !!query.matches;
}
_setupOpenCloseListeners() {
super._setupOpenCloseListeners();
if (this._overlayInvokerNode) {
this._overlayInvokerNode.addEventListener('click', this.__toggle);
}
}
_teardownOpenCloseListeners() {
super._teardownOpenCloseListeners();
if (this._overlayInvokerNode) {
this._overlayInvokerNode.removeEventListener('click', this.__toggle);
}
}
__toggle() {
this.opened = !this.opened;
}
// ********************* GESTURE ***********************
constructor() {
super();
this.useOverlay = false;
this.useOverlayMediaQuery = '(max-width: 1024px)';
this.__toggle = this.__toggle.bind(this);
this.onMatchMedia = this.onMatchMedia.bind(this);
this.onGestureStart = this.onGestureStart.bind(this);
this.onGestureMove = this.onGestureMove.bind(this);
this.onGestureEnd = this.onGestureEnd.bind(this);
this.updateFromTouch = this.updateFromTouch.bind(this);
this.mediaMatcher = window.matchMedia(this.useOverlayMediaQuery);
this.mediaMatcher.addEventListener('change', this.onMatchMedia);
this._startX = 0;
this._currentX = 0;
this._velocity = 0;
this._left = 0;
this.__touching = false;
this._timestamp = 0;
}
connectedCallback() {
super.connectedCallback();
this.useOverlay = !!this.mediaMatcher.matches;
}
render() {
return html`
<slot name="invoker"></slot>
<slot name="_overlay-shadow-outlet"></slot>
<div id="overlay-content-node-wrapper">
<slot name="content"></slot>
</div>
`;
}
/**
* @param {TouchEvent} ev
*/
onGestureStart(ev) {
if (!this.containerEl) {
return;
}
this.__touching = true;
this._left = this.containerEl.getBoundingClientRect().left;
this._startX = ev.targetTouches[0].clientX;
this._currentX = this._startX;
this._timestamp = new Date().getTime();
this._velocity = 0;
this._overlayCtrl.contentNode.style.transition = '';
document.body.addEventListener('touchmove', this.onGestureMove, { passive: true });
document.body.addEventListener('touchend', this.onGestureEnd, { passive: true });
document.body.addEventListener('touchcancel', this.onGestureEnd, { passive: true });
requestAnimationFrame(this.updateFromTouch);
}
/**
* @param {number} dDist
* @param {number} dTime
*/
addVelocitySample(dDist, dTime) {
if (dTime === 0) {
return;
}
const velocitySample = dDist / dTime;
// Low pass filter.
const alpha = 0.75;
this._velocity *= alpha;
this._velocity += (1 - alpha) * velocitySample;
}
/**
* @param {TouchEvent} ev
*/
onGestureMove(ev) {
if (!this.__touching) {
return;
}
const lastTimestamp = this._timestamp;
this._timestamp = new Date().getTime();
const dTime = this._timestamp - lastTimestamp;
const lastX = this._currentX;
this._currentX = ev.targetTouches[0].clientX;
const dX = this._currentX - lastX;
this.addVelocitySample(dX, dTime);
}
onGestureEnd() {
if (!this.__touching || !this.containerEl) {
this.opened = false;
return;
}
this.__touching = false;
let endOpenedState;
// Check for fling.
if (Math.abs(this._velocity) > 1) {
endOpenedState = this._velocity > 0;
} else {
// Check depending on percentage visible.
const { left } = this.containerEl.getBoundingClientRect();
const width = this.containerEl.clientWidth;
const percentageVisible = (left + width) / width;
endOpenedState = percentageVisible >= 0.5;
}
this._overlayCtrl.contentNode.style.transition =
'transform 0.20s cubic-bezier(0.4, 0.0, 0.2, 1)';
this.containerEl.style.transform = '';
this.opened = endOpenedState;
document.body.removeEventListener('touchmove', this.onGestureMove);
document.body.removeEventListener('touchend', this.onGestureEnd);
document.body.removeEventListener('touchcancel', this.onGestureEnd);
}
updateFromTouch() {
if (!this.__touching || !this.containerEl) {
return;
}
requestAnimationFrame(this.updateFromTouch);
const translateX = Math.min(0, this._currentX - this._startX + this._left);
this.containerEl.style.transform = `translateX(${translateX}px)`;
}
}

View File

@@ -1,24 +0,0 @@
// Don't edit this file directly. It is generated by /scripts/update-package-configs.ts
{
"extends": "../../tsconfig.browser-base.json",
"compilerOptions": {
"module": "ESNext",
"outDir": "./dist-types",
"rootDir": ".",
"composite": true,
"allowJs": true,
"checkJs": true,
"emitDeclarationOnly": true
},
"references": [],
"include": [
"src",
"*.js",
"types"
],
"exclude": [
"dist",
"dist-types"
]
}

View File

@@ -1,5 +1,51 @@
# @rocket/engine # @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
# &lt;some-button>
```
## 0.2.4
### Patch Changes
- 09a47b4: Prevent fatal error because of simultaneous write to file.
When the browser requested a file to be rendered and that file also needed an update in the "rocket header" (the top of the file) then it could be that the watcher trigger a simultaneous render of the file while the first render was still in progress.
The solution is that the watcher ignores changes to a file until a full render is finished.
## 0.2.3
### Patch Changes
- 379f08f: Remove the lit workaround to globally load the `global-dom-shim` in the "main thread".
Which means only the worker that does the actual SSR rendering will load it.
## 0.2.2
### Patch Changes
- 6f88d8e: Get rid of the `rehype-prism` workaround by using latest esm version of mdjs that uses `rehype-prism-plus`
- Updated dependencies [35ed64d]
- Updated dependencies [6f88d8e]
- @mdjs/core@0.20.0
## 0.2.1 ## 0.2.1
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "@rocket/engine", "name": "@rocket/engine",
"version": "0.2.1", "version": "0.2.5",
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },
@@ -35,9 +35,9 @@
"scripts": { "scripts": {
"debug": "DEBUG=engine:rendering yarn test", "debug": "DEBUG=engine:rendering yarn test",
"debug:integration": "PWDEBUG=1 yarn test:integration", "debug:integration": "PWDEBUG=1 yarn test:integration",
"test": "mocha --require ../../scripts/testMochaGlobalHooks.js --timeout 5000 test-node/**/*.test.{js,cjs} test-node/*.test.{js,cjs}", "test": "mocha --require ../../scripts/testMochaGlobalHooks.js --timeout 8000 test-node/**/*.test.js test-node/*.test.js",
"test:integration": "playwright test test-node/*.spec.js", "test:integration": "playwright test test-node/*.spec.js",
"test:watch": "onchange 'src/**/*.{js,cjs}' 'test-node/**/*.{js,cjs}' -- npm test", "test:watch": "onchange 'src/**/*.js' 'test-node/**/*.js' -- npm test",
"types:copy": "copyfiles \"./types/**/*.d.ts\" dist-types/" "types:copy": "copyfiles \"./types/**/*.d.ts\" dist-types/"
}, },
"files": [ "files": [
@@ -47,14 +47,15 @@
], ],
"dependencies": { "dependencies": {
"@d4kmor/tree-model": "^0.1.3", "@d4kmor/tree-model": "^0.1.3",
"@lit-labs/ssr": "^2.0.4", "@lit-labs/ssr": "^2.2.3",
"@mdjs/core": "^0.9.5", "@mdjs/core": "^0.20.0",
"@parcel/watcher": "^2.0.5", "@parcel/watcher": "^2.0.5",
"@web/dev-server": "^0.1.4", "@web/dev-server": "^0.1.4",
"es-module-lexer": "^0.9.3", "es-module-lexer": "^0.10.5",
"lit": "^2.2.5", "lit": "^2.3.0",
"plugins-manager": "^0.3.0", "plugins-manager": "^0.3.0",
"sax-wasm": "^2.1.3" "sax-wasm": "^2.1.3",
"unist-util-visit": "^4.1.0"
}, },
"types": "./dist-types/src/index.d.ts", "types": "./dist-types/src/index.d.ts",
"typesVersions": { "typesVersions": {

View File

@@ -444,7 +444,9 @@ export class Engine {
} }
return result; return result;
} }
if (this.watcher) {
this.watcher.addFileToIgnore(sourceFilePath);
}
if (rocketHeader) { if (rocketHeader) {
const { needsAnotherRenderingPass } = await rocketHeader.syncComponents({ const { needsAnotherRenderingPass } = await rocketHeader.syncComponents({
outputFileContent: result.fileContent, outputFileContent: result.fileContent,
@@ -460,6 +462,9 @@ export class Engine {
} }
} }
if (this.watcher) {
this.watcher.removeFileToIgnore(sourceFilePath);
}
return result; return result;
} }
} }

View File

@@ -78,6 +78,11 @@ export class Watcher {
acceptPageUpdates = true; acceptPageUpdates = true;
/**
* @type {Set<string>}
*/
_filesToIgnore = new Set();
/** /**
* @type {Map<string, { type: string, jsDependencies?: string[], webSockets?: Set<import('@web/dev-server-core').WebSocket> }>} * @type {Map<string, { type: string, jsDependencies?: string[], webSockets?: Set<import('@web/dev-server-core').WebSocket> }>}
*/ */
@@ -96,6 +101,9 @@ export class Watcher {
async (err, events) => { async (err, events) => {
if (this.acceptPageUpdates) { if (this.acceptPageUpdates) {
for (const event of events) { for (const event of events) {
if (this.isIgnoredFile(event.path)) {
return;
}
if (event.type === 'create') { if (event.type === 'create') {
await this.addCreateTask(event.path); await this.addCreateTask(event.path);
} }
@@ -109,6 +117,9 @@ export class Watcher {
await this.executeTaskQueue(); await this.executeTaskQueue();
} else { } else {
for (const event of events) { for (const event of events) {
if (this.isIgnoredFile(event.path)) {
return;
}
if ( if (
this._taskQueue.has(event.path) || this._taskQueue.has(event.path) ||
// we exclude files here as `@parcel/watcher` does not support globs in `ignore` // we exclude files here as `@parcel/watcher` does not support globs in `ignore`
@@ -340,4 +351,26 @@ export class Watcher {
this._taskQueue.clear(); this._taskQueue.clear();
this.acceptPageUpdates = true; this.acceptPageUpdates = true;
} }
/**
* @param {string} filePath
*/
addFileToIgnore(filePath) {
this._filesToIgnore.add(filePath);
}
/**
* @param {string} filePath
*/
removeFileToIgnore(filePath) {
this._filesToIgnore.delete(filePath);
}
/**
* @param {string} filePath
* @returns {boolean}
*/
isIgnoredFile(filePath) {
return this._filesToIgnore.has(filePath);
}
} }

View File

@@ -1,8 +1,3 @@
// we load this before the global-dom-shim as otherwise prism thinks it's running in a browser 🙈
// we need to load the global-dom-shim as otherwise import { html } from 'lit'; breaks
import 'rehype-prism';
import '@lit-labs/ssr/lib/install-global-dom-shim.js';
/* eslint-disable @typescript-eslint/ban-ts-comment */ /* eslint-disable @typescript-eslint/ban-ts-comment */
// @ts-ignore // @ts-ignore
import { mdjsProcess } from '@mdjs/core'; import { mdjsProcess } from '@mdjs/core';
@@ -11,7 +6,7 @@ import { readFile, writeFile } from 'fs/promises';
import path from 'path'; import path from 'path';
import { addPlugin } from 'plugins-manager'; import { addPlugin } from 'plugins-manager';
import markdown from 'remark-parse'; import markdown from 'remark-parse';
import visit from 'unist-util-visit'; import { visit } from 'unist-util-visit';
/** /**
* @param {string} string * @param {string} string
@@ -23,7 +18,7 @@ function escapeRegExp(string) {
const REGEX_REPLACE_ESCAPES = new RegExp( const REGEX_REPLACE_ESCAPES = new RegExp(
escapeRegExp( escapeRegExp(
'\\\\</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>', '\\\\<span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>',
), ),
'g', 'g',
); );
@@ -146,7 +141,7 @@ export async function mdInJsToMdHtmlInJs(toImportFilePath) {
// this corrects it - escaped // this corrects it - escaped
mdHTML = mdHTML.replace( mdHTML = mdHTML.replace(
REGEX_REPLACE_ESCAPES, REGEX_REPLACE_ESCAPES,
'</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">\\\\${</span>', '<span class="token interpolation"><span class="token interpolation-punctuation punctuation">\\\\${</span>',
); );
return [mdHTML, ...mdjsScriptTag].join('\n'); return [mdHTML, ...mdjsScriptTag].join('\n');
} }

View File

@@ -1,8 +1,3 @@
// we load this before the global-dom-shim as otherwise prism thinks it's running in a browser 🙈
// we need to load the global-dom-shim as otherwise import { html } from 'lit'; breaks
import 'rehype-prism';
import '@lit-labs/ssr/lib/install-global-dom-shim.js';
export { renderJoiningGroup } from './helpers/renderJoiningGroup.js'; export { renderJoiningGroup } from './helpers/renderJoiningGroup.js';
export { inlineFile } from './helpers/inlineFile.js'; export { inlineFile } from './helpers/inlineFile.js';

View File

@@ -29,9 +29,23 @@ export function getHtmlMetaData(htmlFilePath) {
// headlinesWithId: [], // headlinesWithId: [],
}; };
/** @type {string | undefined} */
let capturedHeadlineText = undefined;
parser.eventHandler = (ev, _data) => { 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) { if (ev === SaxEventType.CloseTag) {
const data = /** @type {Tag} */ (/** @type {any} */ (_data)); const data = /** @type {Tag} */ (/** @type {any} */ (_data));
// ********** <meta name="*" content="*">
if (data.name === 'meta') { if (data.name === 'meta') {
const metaName = getAttribute(data, 'name'); const metaName = getAttribute(data, 'name');
if (metaName === 'menu:link.text') { if (metaName === 'menu:link.text') {
@@ -63,26 +77,32 @@ export function getHtmlMetaData(htmlFilePath) {
if (!metaData.title && data.name === 'title') { if (!metaData.title && data.name === 'title') {
metaData.title = getText(data); metaData.title = getText(data);
} }
if (!metaData.h1 && data.name === 'h1') {
metaData.h1 = getText(data);
}
// ********** <h1> - <h6>
if (isHeadline(data)) { if (isHeadline(data)) {
const id = getAttribute(data, 'id'); const id = getAttribute(data, 'id');
const rawText = getText(data);
const linkText = getAttribute(data, 'link-text'); const linkText = getAttribute(data, 'link-text');
if (id && rawText) { const processedCapturedHeadlineText = capturedHeadlineText
?.replace(/&#x3C;/g, '&lt;')
.replace(/&#x26;/g, '&')
.trim();
const text = linkText || processedCapturedHeadlineText || '';
if (data.name === 'h1') {
metaData.h1 = text;
}
if (id && text) {
if (!metaData.headlinesWithId) { if (!metaData.headlinesWithId) {
metaData.headlinesWithId = []; metaData.headlinesWithId = [];
} }
const rawTextObj = linkText ? { rawText } : {}; const rawTextObj = linkText ? { rawText: processedCapturedHeadlineText } : {};
metaData.headlinesWithId.push({ metaData.headlinesWithId.push({
text: linkText || rawText, text,
id, id,
level: parseInt(data.name[1], 10), level: parseInt(data.name[1], 10),
...rawTextObj, ...rawTextObj,
}); });
} }
capturedHeadlineText = undefined;
} }
} }
}; };
@@ -97,6 +117,7 @@ export function getHtmlMetaData(htmlFilePath) {
}); });
readable.on('end', () => { readable.on('end', () => {
parser.end(); parser.end();
capturedHeadlineText = undefined;
resolve(metaData); resolve(metaData);
}); });

View File

@@ -9,6 +9,9 @@ const require = createRequire(import.meta.url);
export const streamOptions = { highWaterMark: 128 * 1024 }; export const streamOptions = { highWaterMark: 128 * 1024 };
const saxPath = require.resolve('sax-wasm/lib/sax-wasm.wasm'); const saxPath = require.resolve('sax-wasm/lib/sax-wasm.wasm');
const saxWasmBuffer = await readFile(saxPath); 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); await parser.prepareWasm(saxWasmBuffer);

View File

@@ -1,8 +1,3 @@
// we load this before the global-dom-shim as otherwise prism thinks it's running in a browser 🙈
// we need to load the global-dom-shim as otherwise import { html } from 'lit'; breaks
import 'rehype-prism';
import '@lit-labs/ssr/lib/install-global-dom-shim.js';
import { parentPort } from 'worker_threads'; import { parentPort } from 'worker_threads';
// import { convertMdFile, convertHtmlFile } from '../converts.js'; // import { convertMdFile, convertHtmlFile } from '../converts.js';

View File

@@ -1,8 +1,3 @@
// we load this before the global-dom-shim as otherwise prism thinks it's running in a browser 🙈
// we need to load the global-dom-shim as otherwise import { html } from 'lit'; breaks
import 'rehype-prism';
import '@lit-labs/ssr/lib/install-global-dom-shim.js';
import path from 'path'; import path from 'path';
import { parentPort } from 'worker_threads'; import { parentPort } from 'worker_threads';
import { mkdir, writeFile } from 'fs/promises'; import { mkdir, writeFile } from 'fs/promises';

View File

@@ -72,10 +72,10 @@ describe('Format Markdown', () => {
'<p>Escape JS</p>', '<p>Escape JS</p>',
'<pre', '<pre',
' class="language-js"', ' class="language-js"',
'><code class="language-js"><span class="token keyword">const</span> foo <span class="token operator">=</span> <span class="token string">\'one\'</span><span class="token punctuation">;</span>', '><code class="language-js code-highlight"><span class="code-line"><span class="token keyword">const</span> foo <span class="token operator">=</span> <span class="token string">\'one\'</span><span class="token punctuation">;</span>',
'<span class="token keyword">const</span> bar <span class="token operator">=</span> html<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">&#x3C;p>${foo}&#x3C;/p></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>', '</span><span class="code-line"><span class="token keyword">const</span> bar <span class="token operator">=</span> html<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token html language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&#x3C;</span>p</span><span class="token punctuation">></span></span>${foo}<span class="token tag"><span class="token tag"><span class="token punctuation">&#x3C;/</span>p</span><span class="token punctuation">></span></span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>',
'<span class="token keyword">const</span> baz <span class="token operator">=</span> html<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">&#x3C;span></span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">\\${</span>foo<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&#x3C;/span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>', '</span><span class="code-line"><span class="token keyword">const</span> baz <span class="token operator">=</span> html<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token html language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&#x3C;</span>span</span><span class="token punctuation">></span></span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">\\${</span>foo<span class="token interpolation-punctuation punctuation">}</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&#x3C;/</span>span</span><span class="token punctuation">></span></span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>',
'</code></pre>', '</span></code></pre>',
].join('\n'), ].join('\n'),
); );
}); });

View File

@@ -1,5 +1,4 @@
import chai from 'chai'; import chai from 'chai';
// import '@lit-labs/ssr/lib/install-global-dom-shim.js';
import { html } from 'lit'; import { html } from 'lit';
import { renderJoiningGroup } from '../src/helpers/renderJoiningGroup.js'; import { renderJoiningGroup } from '../src/helpers/renderJoiningGroup.js';
import { testLitServerRender } from './test-helpers.js'; import { testLitServerRender } from './test-helpers.js';

View File

@@ -834,4 +834,99 @@ describe('Engine menus', () => {
await cleanup(); 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: '&lt;some-button>',
headlinesWithId: [
{
id: 'some-button',
level: 1,
text: '&lt;some-button>',
},
],
level: 1,
menuLinkText: '&lt;some-button>',
name: '&lt;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: '/',
});
});
}); });

View File

@@ -114,4 +114,77 @@ describe('Engine start error handling', () => {
); );
await cleanup(); await cleanup();
}); });
it('04: update-header-while-rendering', async () => {
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',
[
'/* START - Rocket auto generated - do not touch */',
"export const sourceRelativeFilePath = 'index.rocket.js';",
"import { html, components, layout } from './recursive.data.js';",
'export { html, components, layout };',
'export async function registerCustomElements() {',
' // hydrate-able components',
" customElements.define('hello-typer', await import('#c/HelloTyper.js').then(m => m.HelloTyper));",
'}',
'export const needsLoader = true;',
'/* END - Rocket auto generated - do not touch */',
'',
'export default () => html`',
' <h1>Hello World</h1>',
' <hello-typer loading="hydrate:onVisible"></hello-typer>',
'`;',
].join('\n'),
);
await anEngineEvent('rocketUpdated');
expect(readOutput('index.html')).to.equal(
[
'<!DOCTYPE html>',
'<html lang="en">',
' <head>',
' <meta charset="utf-8" />',
' </head>',
' <body>',
' <h1>Hello World</h1>',
' <hello-typer loading="hydrate:onVisible"',
' ><template shadowroot="open"',
' ><style>',
' button {',
' font-size: 200%;',
' width: 64px;',
' height: 64px;',
' border: none;',
' border-radius: 10px;',
' background-color: seagreen;',
' color: white;',
' }',
' </style>',
' <p>🤔 Hello <span> </span></p>',
' <button>+</button>',
' </template></hello-typer',
' >',
' <script type="module" src="index-loader-generated.js"></script>',
' </body>',
'</html>',
].join('\n'),
);
await cleanup();
});
}); });

View File

@@ -1,15 +1,15 @@
{ {
"title": "Fixed Title", "title": "Fixed Title",
"h1": "\n Welcome Members:\n ", "h1": "Welcome Members:",
"headlinesWithId": [ "headlinesWithId": [
{ {
"text": "\n Welcome Members:\n ", "text": "Welcome Members:",
"id": "welcome-members", "id": "welcome-members",
"level": 1 "level": 1
} }
], ],
"name": "Fixed Title", "name": "Fixed Title",
"menuLinkText": "\n Welcome Members:\n ", "menuLinkText": "Welcome Members:",
"url": "/", "url": "/",
"outputRelativeFilePath": "index.html", "outputRelativeFilePath": "index.html",
"sourceRelativeFilePath": "index.rocket.js", "sourceRelativeFilePath": "index.rocket.js",

View File

@@ -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>`;

View File

@@ -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
}

View File

@@ -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 };

View File

@@ -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 */
```
# &lt;some-button>

View File

@@ -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

View File

@@ -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": "&lt;some-button>",
"headlinesWithId": [
{
"text": "&lt;some-button>",
"id": "some-button",
"level": 1
}
],
"name": "&lt;some-button>",
"menuLinkText": "&lt;some-button>",
"url": "/component/",
"outputRelativeFilePath": "component/index.html",
"sourceRelativeFilePath": "component.rocket.md",
"level": 1
}
]
}

View File

@@ -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 };

View File

@@ -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>
`;

View File

@@ -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
}

View File

@@ -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 };

View File

@@ -0,0 +1,54 @@
import { LitElement, html, css } from 'lit';
let i = 0;
const fullText = [...'to this wonderful world of progressive hydration 🤯'];
export class HelloTyper extends LitElement {
static properties = {
msg: { type: String },
counter: { type: Number },
};
constructor() {
super();
this.msg = ' ';
this.counter = 0;
}
updated(changedProperties) {
super.updated(changedProperties);
if (i < fullText.length) {
setTimeout(() => {
this.msg += fullText[i];
i += 1;
}, Math.floor(Math.random() * 50) + 40);
}
}
render() {
return html`
<p>🤔 Hello <span>${this.msg}</span>${'🤯'.repeat(this.counter)}</p>
<button @click=${this._inc}>+</button>
`;
}
_inc() {
if (i >= fullText.length) {
this.counter += 1;
}
}
static styles = [
css`
button {
font-size: 200%;
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
`,
];
}

View File

@@ -0,0 +1,15 @@
/* START - Rocket auto generated - do not touch */
export const sourceRelativeFilePath = 'index.rocket.js';
import { html, components, layout } from './recursive.data.js';
export { html, components, layout };
export async function registerCustomElements() {
// hydrate-able components
customElements.define('hello-typer', await import('#c/HelloTyper.js').then(m => m.HelloTyper));
}
export const needsLoader = true;
/* END - Rocket auto generated - do not touch */
export default () => html`
<h1>Hello World</h1>
<hello-typer loading="hydrate:onVisible"></hello-typer>
`;

View File

@@ -0,0 +1,22 @@
{
"url": "/",
"outputRelativeFilePath": "index.html",
"sourceRelativeFilePath": "index.rocket.js",
"level": 0,
"children": [
{
"url": "/about/",
"outputRelativeFilePath": "about/index.html",
"sourceRelativeFilePath": "about.rocket.js",
"level": 1
}
],
"menuLinkText": "Hello World",
"name": "Hello World",
"title": "name is not defined",
"h1": "Hello World",
"components": {
"hello-typer": "#c/HelloTyper.js::HelloTyper"
},
"needsLoader": true
}

View File

@@ -0,0 +1,19 @@
import { html } from 'lit';
export { html };
export const components = {
'hello-typer': '#c/HelloTyper.js::HelloTyper',
};
export const layout = data => html`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
</head>
<body>
${data.content()}
</body>
</html>
`;

View File

@@ -0,0 +1,7 @@
{
"name": "@test/components",
"type": "module",
"imports": {
"#c/*": "./components/*"
}
}

View File

@@ -14,6 +14,9 @@
"references": [ "references": [
{ {
"path": "../plugins-manager/tsconfig.json" "path": "../plugins-manager/tsconfig.json"
},
{
"path": "../mdjs-core/tsconfig.json"
} }
], ],
"include": [ "include": [

View File

@@ -50,7 +50,7 @@
"@rocket/components": "^0.2.0", "@rocket/components": "^0.2.0",
"@rocket/engine": "^0.2.0", "@rocket/engine": "^0.2.0",
"@webcomponents/template-shadowroot": "^0.1.0", "@webcomponents/template-shadowroot": "^0.1.0",
"lit": "^2.2.5", "lit": "^2.3.0",
"workbox-window": "^6.1.5" "workbox-window": "^6.1.5"
}, },
"types": "./dist-types/src/index.d.ts", "types": "./dist-types/src/index.d.ts",

View File

@@ -15,6 +15,9 @@
{ {
"path": "../plugins-manager/tsconfig.json" "path": "../plugins-manager/tsconfig.json"
}, },
{
"path": "../mdjs-core/tsconfig.json"
},
{ {
"path": "../engine/tsconfig.json" "path": "../engine/tsconfig.json"
}, },

View File

@@ -1,5 +1,12 @@
# Change Log # Change Log
## 0.20.0
### Minor Changes
- 35ed64d: BREAKING: Refactor to ESM-only package, use latest rehype/remark/unified dependencies.
- 6f88d8e: BREAKING: Replace `rehype-prism` with `rehype-prism-plus` as it does not get confused as running in the browser in the SSR context.
## 0.9.5 ## 0.9.5
### Patch Changes ### Patch Changes

View File

@@ -2,18 +2,11 @@
/** @typedef {import('./types/code.js').Story} Story */ /** @typedef {import('./types/code.js').Story} Story */
/** @typedef {import('./types/code.js').MdjsProcessPlugin} MdjsProcessPlugin */ /** @typedef {import('./types/code.js').MdjsProcessPlugin} MdjsProcessPlugin */
const { mdjsParse } = require('./src/mdjsParse.js'); import { mdjsParse } from './src/mdjsParse.js';
const { mdjsSetupCode } = require('./src/mdjsSetupCode.js'); import { mdjsSetupCode } from './src/mdjsSetupCode.js';
const { mdjsStoryParse } = require('./src/mdjsStoryParse.js'); import { mdjsStoryParse } from './src/mdjsStoryParse.js';
const { mdjsDocPage } = require('./src/mdjsDocPage.js'); import { mdjsDocPage } from './src/mdjsDocPage.js';
const { mdjsProcess } = require('./src/mdjsProcess.js'); import { mdjsProcess } from './src/mdjsProcess.js';
const { isMdjsContent } = require('./src/isMdjsContent.js'); import { isMdjsContent } from './src/isMdjsContent.js';
module.exports = { export { mdjsParse, mdjsStoryParse, mdjsDocPage, mdjsProcess, isMdjsContent, mdjsSetupCode };
mdjsParse,
mdjsStoryParse,
mdjsDocPage,
mdjsProcess,
isMdjsContent,
mdjsSetupCode,
};

View File

@@ -1,6 +0,0 @@
import cjsEntrypoint from './index.js';
const { mdjsParse, mdjsStoryParse, mdjsDocPage, mdjsProcess, isMdjsContent, mdjsSetupCode } =
cjsEntrypoint;
export { mdjsParse, mdjsStoryParse, mdjsDocPage, mdjsProcess, isMdjsContent, mdjsSetupCode };

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mdjs/core", "name": "@mdjs/core",
"version": "0.9.5", "version": "0.20.0",
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },
@@ -14,11 +14,11 @@
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)", "author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
"homepage": "https://rocket.modern-web.dev/docs/markdown-javascript/overview/", "homepage": "https://rocket.modern-web.dev/docs/markdown-javascript/overview/",
"main": "./index.js", "main": "./index.js",
"type": "module",
"exports": { "exports": {
".": { ".": {
"types": "./dist-types/index.d.ts", "types": "./dist-types/index.d.ts",
"require": "./index.js", "default": "./index.js"
"default": "./index.mjs"
} }
}, },
"scripts": { "scripts": {
@@ -46,32 +46,32 @@
"remark" "remark"
], ],
"dependencies": { "dependencies": {
"@mdjs/mdjs-preview": "^0.5.8", "@mdjs/mdjs-preview": "^0.5.9",
"@mdjs/mdjs-story": "^0.3.2", "@mdjs/mdjs-story": "^0.3.2",
"@types/unist": "^2.0.3", "@types/unist": "^2.0.6",
"es-module-lexer": "^0.9.3", "es-module-lexer": "^0.10.5",
"github-markdown-css": "^4.0.0", "github-markdown-css": "^5.1.0",
"plugins-manager": "^0.3.0", "plugins-manager": "^0.3.1",
"rehype-autolink-headings": "^5.0.1", "rehype-autolink-headings": "^6.1.1",
"rehype-prism": "^1.0.1", "rehype-prism-plus": "^1.4.2",
"rehype-raw": "^5.0.0", "rehype-raw": "^6.1.1",
"rehype-slug": "^4.0.1", "rehype-slug": "^5.0.1",
"rehype-stringify": "^8.0.0", "rehype-stringify": "^9.0.3",
"remark": "^13.0.0", "remark": "^14.0.2",
"remark-gfm": "^1.0.0", "remark-gfm": "^3.0.1",
"remark-parse": "^9.0.0", "remark-parse": "^10.0.1",
"remark-rehype": "^8.0.0", "remark-rehype": "^10.1.0",
"slash": "^3.0.0", "slash": "^4.0.0",
"unified": "^9.2.0", "unified": "^10.1.2",
"unist-util-remove": "^2.0.1", "unist-util-remove": "^3.1.0",
"unist-util-visit": "^2.0.3" "unist-util-visit": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"demo-wc-card": "^0.1.0", "demo-wc-card": "^0.1.0",
"remark-autolink-headings": "^6.0.1", "remark-autolink-headings": "^7.0.1",
"remark-html": "^13.0.2", "remark-html": "^15.0.1",
"remark-slug": "^6.0.0", "remark-slug": "^7.0.1",
"remark-stringify": "^9.0.1" "remark-stringify": "^10.0.2"
}, },
"types": "dist-types/index.d.ts" "types": "dist-types/index.d.ts"
} }

View File

@@ -4,7 +4,7 @@
* @param {string} text * @param {string} text
* @returns {boolean} * @returns {boolean}
*/ */
function isMdjsContent(text) { export function isMdjsContent(text) {
if (!text) { if (!text) {
return false; return false;
} }
@@ -18,7 +18,3 @@ function isMdjsContent(text) {
return false; return false;
} }
} }
module.exports = {
isMdjsContent,
};

View File

@@ -1,11 +1,11 @@
const { mdjsProcess } = require('./mdjsProcess.js'); import { mdjsProcess } from './mdjsProcess.js';
/** /**
* *
* @param {string} body * @param {string} body
* @returns {Promise<string>} * @returns {Promise<string>}
*/ */
async function mdjsDocPage(body) { export async function mdjsDocPage(body) {
const data = await mdjsProcess(body); const data = await mdjsProcess(body);
return ` return `
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
@@ -92,7 +92,3 @@ async function mdjsDocPage(body) {
</div> </div>
`; `;
} }
module.exports = {
mdjsDocPage,
};

View File

@@ -1,12 +1,10 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */ import { visit } from 'unist-util-visit';
const visit = require('unist-util-visit'); import { remove } from 'unist-util-remove';
// @ts-ignore
const remove = require('unist-util-remove');
/** @typedef {import('vfile').VFileOptions} VFileOptions */ /** @typedef {import('vfile').VFileOptions} VFileOptions */
/** @typedef {import('unist').Node} Node */ /** @typedef {import('unist').Node} Node */
function mdjsParse() { export function mdjsParse() {
let jsCode = ''; let jsCode = '';
/** /**
@@ -14,26 +12,37 @@ function mdjsParse() {
* @param {VFileOptions} file * @param {VFileOptions} file
*/ */
function transformer(tree, file) { function transformer(tree, file) {
visit(tree, 'code', node => { visit(
tree,
'code',
/** @param {Node & {[key: string]: unknown;}} node */ node => {
if (node.lang === 'js' && node.meta === 'script') { if (node.lang === 'js' && node.meta === 'script') {
jsCode += node.value; jsCode += node.value;
} }
if (node.lang === 'js' && node.meta === 'client') { if (node.lang === 'js' && node.meta === 'client') {
jsCode += node.value; jsCode += node.value;
} }
}); },
);
// we can only return/modify the tree but jsCode should not be part of the tree // we can only return/modify the tree but jsCode should not be part of the tree
// so we attach it globally to the file.data // so we attach it globally to the file.data
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
if (!file.data) {
file.data = {};
}
file.data.jsCode = jsCode; file.data.jsCode = jsCode;
/** /**
* @param {Node} node * @param {Node} node
*/ */
const removeFunction = node => const removeFunction = node => {
node.type === 'code' && const _node = /** @type {Node & {[key: string]: unknown;}} */ (node);
node.lang === 'js' && return (
(node.meta === 'script' || node.meta === 'client'); _node.type === 'code' &&
_node.lang === 'js' &&
(_node.meta === 'script' || _node.meta === 'client')
);
};
remove(tree, removeFunction); remove(tree, removeFunction);
return tree; return tree;
@@ -41,7 +50,3 @@ function mdjsParse() {
return transformer; return transformer;
} }
module.exports = {
mdjsParse,
};

View File

@@ -4,23 +4,19 @@
/** @typedef {import('../types/code').ProcessResult} ProcessResult */ /** @typedef {import('../types/code').ProcessResult} ProcessResult */
/** @typedef {import('../types/code').MdjsProcessPlugin} MdjsProcessPlugin */ /** @typedef {import('../types/code').MdjsProcessPlugin} MdjsProcessPlugin */
const unified = require('unified'); import { unified } from 'unified';
const markdown = require('remark-parse'); import markdown from 'remark-parse';
const gfm = require('remark-gfm'); import gfm from 'remark-gfm';
const remark2rehype = require('remark-rehype'); import remark2rehype from 'remark-rehype';
const raw = require('rehype-raw'); import raw from 'rehype-raw';
const htmlStringify = require('rehype-stringify'); import htmlSlug from 'rehype-slug';
const htmlSlug = require('rehype-slug'); import htmlHeading from 'rehype-autolink-headings';
const htmlHeading = require('rehype-autolink-headings'); import rehypePrism from 'rehype-prism-plus';
// @ts-ignore import htmlStringify from 'rehype-stringify';
const { executeSetupFunctions } = require('plugins-manager'); import { executeSetupFunctions } from 'plugins-manager';
const loadLanguages = require('prismjs/components/'); import { mdjsParse } from './mdjsParse.js';
import { mdjsStoryParse } from './mdjsStoryParse.js';
const { mdjsParse } = require('./mdjsParse.js'); import { mdjsSetupCode } from './mdjsSetupCode.js';
const { mdjsStoryParse } = require('./mdjsStoryParse.js');
const { mdjsSetupCode } = require('./mdjsSetupCode.js');
let prismLoaded = false;
/** @type {MdjsProcessPlugin[]} */ /** @type {MdjsProcessPlugin[]} */
const defaultMetaPlugins = [ const defaultMetaPlugins = [
@@ -37,6 +33,7 @@ const defaultMetaPlugins = [
{ plugin: htmlSlug, options: {} }, { plugin: htmlSlug, options: {} },
// @ts-ignore // @ts-ignore
{ plugin: htmlHeading, options: {} }, { plugin: htmlHeading, options: {} },
{ plugin: rehypePrism, options: {} },
// @ts-ignore // @ts-ignore
{ plugin: htmlStringify, options: {} }, { plugin: htmlStringify, options: {} },
]; ];
@@ -52,15 +49,8 @@ const defaultMetaPlugins = [
* @param {function[]} [options.setupUnifiedPlugins] * @param {function[]} [options.setupUnifiedPlugins]
* @param {MdjsProcessPlugin[]} [options.plugins] deprecated option use setupUnifiedPlugins instead * @param {MdjsProcessPlugin[]} [options.plugins] deprecated option use setupUnifiedPlugins instead
*/ */
async function mdjsProcess(mdjs, { setupUnifiedPlugins = [] } = {}) { export async function mdjsProcess(mdjs, { setupUnifiedPlugins = [] } = {}) {
const parser = unified(); const parser = unified();
if (!prismLoaded) {
prismLoaded = true;
const rehypePrism = (await import('rehype-prism/lib/src/index.js')).default;
loadLanguages(['md', 'shell', 'yml', 'diff']);
defaultMetaPlugins.splice(6, 0, { plugin: rehypePrism, options: {} });
}
const metaPlugins = executeSetupFunctions(setupUnifiedPlugins, defaultMetaPlugins); const metaPlugins = executeSetupFunctions(setupUnifiedPlugins, defaultMetaPlugins);
for (const pluginObj of metaPlugins) { for (const pluginObj of metaPlugins) {
@@ -74,9 +64,5 @@ async function mdjsProcess(mdjs, { setupUnifiedPlugins = [] } = {}) {
const { stories, setupJsCode } = result.data; const { stories, setupJsCode } = result.data;
return { stories, jsCode: setupJsCode, html: result.contents }; return { stories, jsCode: setupJsCode, html: result.value };
} }
module.exports = {
mdjsProcess,
};

View File

@@ -1,5 +1,5 @@
const path = require('path'); import path from 'path';
const slash = require('slash'); import slash from 'slash';
/** @typedef {import('vfile').VFileOptions} VFileOptions */ /** @typedef {import('vfile').VFileOptions} VFileOptions */
/** @typedef {import('unist').Node} Node */ /** @typedef {import('unist').Node} Node */
@@ -22,7 +22,7 @@ const slash = require('slash');
* @param {rocketConfig} [options.rocketConfig] * @param {rocketConfig} [options.rocketConfig]
* @returns * @returns
*/ */
function mdjsSetupCode({ export function mdjsSetupCode({
rootNodeQueryCode = 'document', rootNodeQueryCode = 'document',
simulationSettings = {}, simulationSettings = {},
rocketConfig = {}, rocketConfig = {},
@@ -43,11 +43,13 @@ function mdjsSetupCode({
* @param {VFileOptions} file * @param {VFileOptions} file
*/ */
async function transformer(tree, file) { async function transformer(tree, file) {
if (!file.data) {
file.data = {};
}
const { stories, jsCode } = file.data; const { stories, jsCode } = file.data;
file.data.setupJsCode = jsCode; file.data.setupJsCode = jsCode;
if (stories && stories.length > 0) { if (Array.isArray(stories) && stories.length > 0) {
const storiesCode = stories.map(/** @param {Story} story */ story => story.code).join('\n'); const storiesCode = stories.map(/** @param {Story} story */ story => story.code).join('\n');
const invokeStoriesCode = []; const invokeStoriesCode = [];
@@ -86,7 +88,3 @@ function mdjsSetupCode({
return transformer; return transformer;
} }
module.exports = {
mdjsSetupCode,
};

View File

@@ -6,8 +6,8 @@
/** @typedef {import('unist').Parent} UnistParent */ /** @typedef {import('unist').Parent} UnistParent */
/** @typedef {import('vfile').VFileOptions} VFileOptions */ /** @typedef {import('vfile').VFileOptions} VFileOptions */
const visit = require('unist-util-visit'); import { visit } from 'unist-util-visit';
const { init, parse } = require('es-module-lexer'); import { init, parse } from 'es-module-lexer';
/** /**
* @typedef {object} MDJSNodeProperties * @typedef {object} MDJSNodeProperties
@@ -50,7 +50,7 @@ function defaultPreviewStoryTag(name) {
* @param {TagFunction} [arg.previewStoryTag] * @param {TagFunction} [arg.previewStoryTag]
* @param {number} [arg.counter] * @param {number} [arg.counter]
*/ */
function mdjsStoryParse({ export function mdjsStoryParse({
storyTag = defaultStoryTag, storyTag = defaultStoryTag,
previewStoryTag = defaultPreviewStoryTag, previewStoryTag = defaultPreviewStoryTag,
} = {}) { } = {}) {
@@ -61,11 +61,12 @@ function mdjsStoryParse({
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
/** /**
* @param {UnistNode} node * @param {UnistNode} _node
* @param {number} index * @param {number} index
* @param {UnistParent} parent * @param {UnistParent} parent
*/ */
const nodeCodeVisitor = (node, index, parent) => { const nodeCodeVisitor = (_node, index, parent) => {
let node = /** @type {UnistNode & {[key: string]: unknown}} */ (_node);
if (node.lang === 'js' && node.meta === 'story' && typeof node.value === 'string') { if (node.lang === 'js' && node.meta === 'story' && typeof node.value === 'string') {
const storyData = extractStoryData(node.value); const storyData = extractStoryData(node.value);
node.type = 'html'; node.type = 'html';
@@ -80,12 +81,17 @@ function mdjsStoryParse({
const inside = [node]; const inside = [node];
let skipAmount = 1; let skipAmount = 1;
const next = parent.children[index + 1];
const next = /** @type {UnistNode & {[key: string]: unknown}} */ (
parent.children[index + 1]
);
if (next && next.type === 'code' && next.meta === 'story-code') { if (next && next.type === 'code' && next.meta === 'story-code') {
inside.push(next); inside.push(next);
skipAmount += 1; skipAmount += 1;
const next2 = parent.children[index + 2]; const next2 = /** @type {UnistNode & {[key: string]: unknown}} */ (
parent.children[index + 2]
);
if (next2 && next2.type === 'code' && next2.meta === 'story-code') { if (next2 && next2.type === 'code' && next2.meta === 'story-code') {
inside.push(next2); inside.push(next2);
skipAmount += 1; skipAmount += 1;
@@ -132,12 +138,16 @@ function mdjsStoryParse({
const tagParts = newValue.split('[[CODE SLOT]]'); const tagParts = newValue.split('[[CODE SLOT]]');
const inside = [node]; const inside = [node];
let skipAmount = 1; let skipAmount = 1;
const next = parent.children[index + 1]; const next = /** @type {UnistNode & {[key: string]: unknown}} */ (
parent.children[index + 1]
);
if (next && next.type === 'code' && next.meta === 'story-code') { if (next && next.type === 'code' && next.meta === 'story-code') {
inside.push(next); inside.push(next);
skipAmount += 1; skipAmount += 1;
const next2 = parent.children[index + 2]; const next2 = /** @type {UnistNode & {[key: string]: unknown}} */ (
parent.children[index + 2]
);
if (next2 && next2.type === 'code' && next2.meta === 'story-code') { if (next2 && next2.type === 'code' && next2.meta === 'story-code') {
inside.push(next2); inside.push(next2);
skipAmount += 1; skipAmount += 1;
@@ -176,6 +186,9 @@ function mdjsStoryParse({
visit(tree, 'code', nodeCodeVisitor); visit(tree, 'code', nodeCodeVisitor);
// we can only return/modify the tree but stories should not be part of the tree // we can only return/modify the tree but stories should not be part of the tree
// so we attach it globally to the file.data // so we attach it globally to the file.data
if (!file.data) {
file.data = {};
}
file.data.stories = stories; file.data.stories = stories;
return tree; return tree;
@@ -184,7 +197,3 @@ function mdjsStoryParse({
return transformer; return transformer;
/* eslint-enable no-param-reassign */ /* eslint-enable no-param-reassign */
} }
module.exports = {
mdjsStoryParse,
};

View File

@@ -1,22 +1,20 @@
/* eslint-disable no-template-curly-in-string */ /* eslint-disable no-template-curly-in-string */
const unified = require('unified'); import { unified } from 'unified';
const markdown = require('remark-parse'); import markdown from 'remark-parse';
const remark2rehype = require('remark-rehype'); import remark2rehype from 'remark-rehype';
const htmlStringify = require('rehype-stringify'); import htmlStringify from 'rehype-stringify';
const htmlSlug = require('rehype-slug'); import htmlSlug from 'rehype-slug';
const htmlHeading = require('rehype-autolink-headings'); import htmlHeading from 'rehype-autolink-headings';
const raw = require('rehype-raw'); import raw from 'rehype-raw';
const mdSlug = require('remark-slug'); import mdSlug from 'remark-slug';
const mdHeadings = require('remark-autolink-headings'); import mdHeadings from 'remark-autolink-headings';
const mdStringify = require('remark-html'); import mdStringify from 'remark-html';
const chai = require('chai'); import { expect } from 'chai';
const { mdjsParse } = require('../src/mdjsParse.js'); import { mdjsParse } from '../src/mdjsParse.js';
const { mdjsStoryParse } = require('../src/mdjsStoryParse.js'); import { mdjsStoryParse } from '../src/mdjsStoryParse.js';
const { expect } = chai;
/** @typedef {import("../src/mdjsParse.js").MDJSVFileData} MDJSVFileData */ /** @typedef {import("../src/mdjsParse.js").MDJSVFileData} MDJSVFileData */
@@ -67,8 +65,8 @@ describe('Integration', () => {
.use(htmlHeading) .use(htmlHeading)
.use(htmlStringify); .use(htmlStringify);
const result = await parser.process(input); const result = await parser.process(input);
if (result.contents instanceof Buffer) throw new Error('contents should not be a buffer'); if (result.value instanceof Buffer) throw new Error('contents should not be a buffer');
expect(result.contents.split('\n')).to.deep.equal(expected); expect(result.value.split('\n')).to.deep.equal(expected);
expect(/** @type {MDJSVFileData} */ (result.data).jsCode).to.equal('const bar = 22;'); expect(/** @type {MDJSVFileData} */ (result.data).jsCode).to.equal('const bar = 22;');
}); });
@@ -108,7 +106,7 @@ describe('Integration', () => {
.use(mdHeadings) .use(mdHeadings)
.use(mdStringify, { sanitize: false }); .use(mdStringify, { sanitize: false });
const result = await parser.process(input); const result = await parser.process(input);
expect(result.contents).to.equal(expected); expect(result.value).to.equal(expected);
expect(/** @type {MDJSVFileData} */ (result.data).stories).to.deep.equal([ expect(/** @type {MDJSVFileData} */ (result.data).stories).to.deep.equal([
{ {
key: 'fooStory', key: 'fooStory',

View File

@@ -1,12 +1,10 @@
/* eslint-disable no-template-curly-in-string */ /* eslint-disable no-template-curly-in-string */
import { adjustPluginOptions } from 'plugins-manager';
import { mdjsProcess } from '../src/mdjsProcess.js';
import { mdjsSetupCode } from '../src/mdjsSetupCode.js';
import { mdjsStoryParse } from '../src/mdjsStoryParse.js';
const chai = require('chai'); import { expect } from 'chai';
const { adjustPluginOptions } = require('plugins-manager');
const { mdjsProcess } = require('../src/mdjsProcess.js');
const { mdjsSetupCode } = require('../src/mdjsSetupCode.js');
const { mdjsStoryParse } = require('../src/mdjsStoryParse.js');
const { expect } = chai;
describe('mdjsProcess', () => { describe('mdjsProcess', () => {
const input = [ const input = [
@@ -28,15 +26,15 @@ describe('mdjsProcess', () => {
it('extracts code blocks with "js story" and "js preview-story" and places marker tags', async () => { it('extracts code blocks with "js story" and "js preview-story" and places marker tags', async () => {
const expected = [ const expected = [
'<p>Intro</p>', '<p>Intro</p>',
'<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> foo <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>', '<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token keyword">const</span> foo <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>',
'</code></pre>', '</span></code></pre>',
'<mdjs-story mdjs-story-name="fooStory"></mdjs-story>', '<mdjs-story mdjs-story-name="fooStory"></mdjs-story>',
'<mdjs-preview mdjs-story-name="fooPreviewStory">', '<mdjs-preview mdjs-story-name="fooPreviewStory">',
'', '',
'', '',
'', '',
'<pre class="language-js"><code class="language-js"><span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">fooPreviewStory</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span>', '<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token keyword module">export</span> <span class="token keyword">const</span> <span class="token function-variable function">fooPreviewStory</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span>',
'</code></pre>', '</span></code></pre>',
'', '',
'', '',
'', '',
@@ -68,7 +66,6 @@ describe('mdjsProcess', () => {
].join('\n'); ].join('\n');
const result = await mdjsProcess(input); const result = await mdjsProcess(input);
expect(result.html).to.equal(expected); expect(result.html).to.equal(expected);
expect(result.jsCode).to.equal(expectedJsCode); expect(result.jsCode).to.equal(expectedJsCode);
}); });
@@ -100,8 +97,8 @@ describe('mdjsProcess', () => {
it('can setup all unified plugins via "setupUnifiedPlugins" which accepts a single function or an array of functions', async () => { it('can setup all unified plugins via "setupUnifiedPlugins" which accepts a single function or an array of functions', async () => {
const expected = [ const expected = [
'<p>Intro</p>', '<p>Intro</p>',
'<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> foo <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>', '<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token keyword">const</span> foo <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>',
'</code></pre>', '</span></code></pre>',
'<my-story mdjs-story-name="fooStory"></my-story>', '<my-story mdjs-story-name="fooStory"></my-story>',
'<my-preview mdjs-story-name="fooPreviewStory"></my-preview>', '<my-preview mdjs-story-name="fooPreviewStory"></my-preview>',
].join('\n'); ].join('\n');
@@ -130,8 +127,8 @@ describe('mdjsProcess', () => {
const expectedForArray = [ const expectedForArray = [
'<p>Intro</p>', '<p>Intro</p>',
'<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> foo <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>', '<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token keyword">const</span> foo <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>',
'</code></pre>', '</span></code></pre>',
'<my-story2 mdjs-story-name="fooStory"></my-story2>', '<my-story2 mdjs-story-name="fooStory"></my-story2>',
'<my-preview2 mdjs-story-name="fooPreviewStory"></my-preview2>', '<my-preview2 mdjs-story-name="fooPreviewStory"></my-preview2>',
].join('\n'); ].join('\n');

View File

@@ -1,11 +1,9 @@
const unified = require('unified'); import { unified } from 'unified';
const markdown = require('remark-parse'); import markdown from 'remark-parse';
const html = require('remark-html'); import html from 'remark-html';
import { mdjsParse } from '../src/mdjsParse.js';
const chai = require('chai'); import { expect } from 'chai';
const { mdjsParse } = require('../src/mdjsParse.js');
const { expect } = chai;
/** @typedef {import("../src/mdjsParse.js").MDJSVFileData} MDJSVFileData */ /** @typedef {import("../src/mdjsParse.js").MDJSVFileData} MDJSVFileData */
@@ -22,7 +20,7 @@ describe('mdjsParse', () => {
].join('\n'); ].join('\n');
const parser = unified().use(markdown).use(mdjsParse).use(html, { sanitize: false }); const parser = unified().use(markdown).use(mdjsParse).use(html, { sanitize: false });
const result = await parser.process(input); const result = await parser.process(input);
expect(result.contents).to.equal( expect(result.value).to.equal(
'<h2>Intro</h2>\n<pre><code class="language-js">const foo = 1;\n</code></pre>\n', '<h2>Intro</h2>\n<pre><code class="language-js">const foo = 1;\n</code></pre>\n',
); );
expect(/** @type {MDJSVFileData} */ (result.data).jsCode).to.equal('const bar = 22;'); expect(/** @type {MDJSVFileData} */ (result.data).jsCode).to.equal('const bar = 22;');
@@ -40,7 +38,7 @@ describe('mdjsParse', () => {
].join('\n'); ].join('\n');
const parser = unified().use(markdown).use(mdjsParse).use(html, { sanitize: false }); const parser = unified().use(markdown).use(mdjsParse).use(html, { sanitize: false });
const result = await parser.process(input); const result = await parser.process(input);
expect(result.contents).to.equal( expect(result.value).to.equal(
'<h2>Intro</h2>\n<pre><code class="language-js">const foo = 1;\n</code></pre>\n', '<h2>Intro</h2>\n<pre><code class="language-js">const foo = 1;\n</code></pre>\n',
); );
expect(/** @type {MDJSVFileData} */ (result.data).jsCode).to.equal('const bar = 22;'); expect(/** @type {MDJSVFileData} */ (result.data).jsCode).to.equal('const bar = 22;');
@@ -56,7 +54,7 @@ describe('mdjsParse', () => {
].join('\n'); ].join('\n');
const parser = unified().use(markdown).use(mdjsParse).use(html, { sanitize: false }); const parser = unified().use(markdown).use(mdjsParse).use(html, { sanitize: false });
const result = await parser.process(input); const result = await parser.process(input);
expect(result.contents).to.equal(''); expect(result.value).to.equal('');
expect(/** @type {MDJSVFileData} */ (result.data).jsCode).to.equal('const bar = 22;'); expect(/** @type {MDJSVFileData} */ (result.data).jsCode).to.equal('const bar = 22;');
}); });
}); });

View File

@@ -1,13 +1,11 @@
/* eslint-disable no-template-curly-in-string */ /* eslint-disable no-template-curly-in-string */
const unified = require('unified'); import { unified } from 'unified';
const markdown = require('remark-parse'); import markdown from 'remark-parse';
const html = require('remark-html'); import html from 'remark-html';
import { mdjsStoryParse } from '../src/mdjsStoryParse.js';
const chai = require('chai'); import { expect } from 'chai';
const { mdjsStoryParse } = require('../src/mdjsStoryParse.js');
const { expect } = chai;
/** @typedef {import("../src/mdjsParse.js").MDJSVFileData} MDJSVFileData */ /** @typedef {import("../src/mdjsParse.js").MDJSVFileData} MDJSVFileData */
@@ -63,7 +61,7 @@ describe('mdjsStoryParse', () => {
const parser = unified().use(markdown).use(mdjsStoryParse).use(html, { sanitize: false }); const parser = unified().use(markdown).use(mdjsStoryParse).use(html, { sanitize: false });
const result = await parser.process(input); const result = await parser.process(input);
expect(result.contents).to.equal(expected); expect(result.value).to.equal(expected);
expect(/** @type {MDJSVFileData} */ (result.data).stories).to.deep.equal([ expect(/** @type {MDJSVFileData} */ (result.data).stories).to.deep.equal([
{ {
key: 'fooStory', key: 'fooStory',
@@ -112,7 +110,7 @@ describe('mdjsStoryParse', () => {
}) })
.use(html, { sanitize: false }); .use(html, { sanitize: false });
const result = await parser.process(input); const result = await parser.process(input);
expect(result.contents).to.equal(expected); expect(result.value).to.equal(expected);
}); });
it('will wrap following story-code blocks', async () => { it('will wrap following story-code blocks', async () => {
@@ -150,7 +148,7 @@ describe('mdjsStoryParse', () => {
const parser = unified().use(markdown).use(mdjsStoryParse).use(html, { sanitize: false }); const parser = unified().use(markdown).use(mdjsStoryParse).use(html, { sanitize: false });
const result = await parser.process(input); const result = await parser.process(input);
expect(result.contents).to.equal(expected); expect(result.value).to.equal(expected);
}); });
it('will wrap following story-code blocks also for html stories', async () => { it('will wrap following story-code blocks also for html stories', async () => {
@@ -188,7 +186,7 @@ describe('mdjsStoryParse', () => {
const parser = unified().use(markdown).use(mdjsStoryParse).use(html, { sanitize: false }); const parser = unified().use(markdown).use(mdjsStoryParse).use(html, { sanitize: false });
const result = await parser.process(input); const result = await parser.process(input);
expect(result.contents).to.equal(expected); expect(result.value).to.equal(expected);
}); });
it('will wrap only following story-code blocks', async () => { it('will wrap only following story-code blocks', async () => {
@@ -244,6 +242,6 @@ describe('mdjsStoryParse', () => {
const parser = unified().use(markdown).use(mdjsStoryParse).use(html, { sanitize: false }); const parser = unified().use(markdown).use(mdjsStoryParse).use(html, { sanitize: false });
const result = await parser.process(input); const result = await parser.process(input);
expect(result.contents).to.equal(expected); expect(result.value).to.equal(expected);
}); });
}); });

View File

@@ -3,7 +3,7 @@
{ {
"extends": "../../tsconfig.node-base.json", "extends": "../../tsconfig.node-base.json",
"compilerOptions": { "compilerOptions": {
"module": "commonjs", "module": "ESNext",
"outDir": "./dist-types", "outDir": "./dist-types",
"rootDir": ".", "rootDir": ".",
"composite": true, "composite": true,

View File

@@ -23,7 +23,7 @@ export interface ProcessResult {
} }
export interface ParseResult { export interface ParseResult {
contents: string; value: string;
data: { data: {
stories: Story[]; stories: Story[];
jsCode: string; jsCode: string;

View File

@@ -27,11 +27,3 @@ declare module 'rehype-autolink-headings' {
export = unified.Plugin; export = unified.Plugin;
} }
declare module 'unist-util-remove' {
import unified from 'unified';
function remove(ast: unified.Node, opts: any, test?: any): unified.Node;
export = remove;
}

View File

@@ -36,7 +36,7 @@
"dependencies": { "dependencies": {
"@lion/accordion": "^0.9.0", "@lion/accordion": "^0.9.0",
"@open-wc/scoped-elements": "^2.0.0", "@open-wc/scoped-elements": "^2.0.0",
"lit": "^2.2.5" "lit": "^2.3.0"
}, },
"types": "dist-types/index.d.ts" "types": "dist-types/index.d.ts"
} }

View File

@@ -31,7 +31,7 @@
"src" "src"
], ],
"dependencies": { "dependencies": {
"lit": "^2.2.5" "lit": "^2.3.0"
}, },
"types": "dist-types/index.d.ts" "types": "dist-types/index.d.ts"
} }

View File

@@ -15,6 +15,9 @@
{ {
"path": "../plugins-manager/tsconfig.json" "path": "../plugins-manager/tsconfig.json"
}, },
{
"path": "../mdjs-core/tsconfig.json"
},
{ {
"path": "../engine/tsconfig.json" "path": "../engine/tsconfig.json"
}, },

View File

@@ -42,7 +42,7 @@
"dependencies": { "dependencies": {
"@rocket/components": "^0.2.0", "@rocket/components": "^0.2.0",
"@rocket/engine": "^0.2.0", "@rocket/engine": "^0.2.0",
"lit": "^2.2.5" "lit": "^2.3.0"
}, },
"devDependencies": {}, "devDependencies": {},
"types": "./dist-types/src/index.d.ts", "types": "./dist-types/src/index.d.ts",

View File

@@ -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: So if you have a page like this:
```md ```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`; You can overwrite that by using the property `menuLinkText`;
````md ````md
```js server ```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: 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. 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 ## Menu No Link
Often you have sections or groups of pages which you want to provide a heading for. Often you have sections or groups of pages which you want to provide a heading for.

View File

@@ -332,7 +332,3 @@ export function myPreset() {
``` ```
</inline-notification> </inline-notification>
```js script
```

View File

@@ -26,7 +26,7 @@ export const needsLoader = true;
# Overview # Overview
```js script ```js client
import '@mdjs/mdjs-story/define'; import '@mdjs/mdjs-story/define';
import '@mdjs/mdjs-preview/define'; import '@mdjs/mdjs-preview/define';
import { html } from '@mdjs/mdjs-story'; import { html } from '@mdjs/mdjs-story';
@@ -34,10 +34,10 @@ import { html } from '@mdjs/mdjs-story';
Markdown JavaScript (mdjs) is a format that allows you to use JavaScript with Markdown, to create interactive demos. It does so by "annotating" JavaScript that should be executed in Markdown. Markdown JavaScript (mdjs) is a format that allows you to use JavaScript with Markdown, to create interactive demos. It does so by "annotating" JavaScript that should be executed in Markdown.
To annotate we use a code block with `js script`. To annotate we use a code block with `js client`.
````md ````md
```js script ```js client
// execute me // execute me
``` ```
```` ````
@@ -69,7 +69,7 @@ You can even execute some JavaScript:
<my-el></my-el> <my-el></my-el>
```js script ```js client
import { LitElement, html } from 'https://unpkg.com/lit-element?module'; import { LitElement, html } from 'https://unpkg.com/lit-element?module';
class MyEl extends LitElement { class MyEl extends LitElement {
@@ -87,7 +87,7 @@ customElements.define('my-el', MyEl);
mdjs comes with some additional helpers you can choose to import: mdjs comes with some additional helpers you can choose to import:
````md ````md
```js script ```js client
import '@mdjs/mdjs-story/define'; import '@mdjs/mdjs-story/define';
import '@mdjs/mdjs-preview/define'; import '@mdjs/mdjs-preview/define';
``` ```

View File

@@ -44,7 +44,7 @@ You can showcase live running code by annotating a code block with `js preview-s
- Settings are ”global” for all Simulators (e.g. changing one will change all) - Settings are ”global” for all Simulators (e.g. changing one will change all)
- Settings can be remembered for other pages / return visits - Settings can be remembered for other pages / return visits
```js script ```js client
import { html } from '@mdjs/mdjs-preview'; import { html } from '@mdjs/mdjs-preview';
import './assets/demo-element.js'; import './assets/demo-element.js';
``` ```
@@ -52,7 +52,7 @@ import './assets/demo-element.js';
## JavaScript Story ## JavaScript Story
````md ````md
```js script ```js client
import { html } from '@mdjs/mdjs-preview'; import { html } from '@mdjs/mdjs-preview';
import './assets/demo-element.js'; import './assets/demo-element.js';
``` ```

View File

@@ -28,12 +28,12 @@ export const needsLoader = true;
You can showcase live running code by annotating a code block with `js story`. You can showcase live running code by annotating a code block with `js story`.
```js script ```js client
import { html } from '@mdjs/mdjs-story'; import { html } from '@mdjs/mdjs-story';
``` ```
````md ````md
```js script ```js client
import { html } from '@mdjs/mdjs-story'; import { html } from '@mdjs/mdjs-story';
``` ```

View File

@@ -7,6 +7,9 @@
{ {
"path": "./packages/plugins-manager/tsconfig.json" "path": "./packages/plugins-manager/tsconfig.json"
}, },
{
"path": "./packages/mdjs-core/tsconfig.json"
},
{ {
"path": "./packages/engine/tsconfig.json" "path": "./packages/engine/tsconfig.json"
}, },

View File

@@ -6,7 +6,7 @@ const packages = [
{ name: 'search', type: 'js', environment: 'node-esm' }, { name: 'search', type: 'js', environment: 'node-esm' },
// { name: 'check-html-links', type: 'js', environment: 'node-esm' }, // { name: 'check-html-links', type: 'js', environment: 'node-esm' },
// { name: 'drawer', type: 'js', environment: 'browser' }, // { name: 'drawer', type: 'js', environment: 'browser' },
// { name: 'mdjs-core', type: 'js', environment: 'node' }, { name: 'mdjs-core', type: 'js', environment: 'node-esm' },
// { name: 'mdjs-preview', type: 'js', environment: 'browser' }, // { name: 'mdjs-preview', type: 'js', environment: 'browser' },
// { name: 'mdjs-story', type: 'js', environment: 'browser' }, // { name: 'mdjs-story', type: 'js', environment: 'browser' },
]; ];

976
yarn.lock

File diff suppressed because it is too large Load Diff