mirror of
https://github.com/modernweb-dev/rocket.git
synced 2026-03-21 15:54:57 +00:00
Compare commits
58 Commits
@rocket/cl
...
@rocket/bu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7261aa2b0 | ||
|
|
690075d335 | ||
|
|
2724f073fc | ||
|
|
d08692c7f3 | ||
|
|
2b7f1ee719 | ||
|
|
3802778be4 | ||
|
|
26f4a1ebff | ||
|
|
81edf45fe2 | ||
|
|
c5a1d7e8d1 | ||
|
|
74dd8d1bcc | ||
|
|
72f631ac86 | ||
|
|
fafb99b0fa | ||
|
|
f5769b9aa9 | ||
|
|
12d9cc3b44 | ||
|
|
ef9b373aa1 | ||
|
|
560234d663 | ||
|
|
024514e901 | ||
|
|
66c2d781e6 | ||
|
|
14721d1e0f | ||
|
|
0f6709ac4b | ||
|
|
ed86ff2346 | ||
|
|
c675820163 | ||
|
|
f4a0ab559f | ||
|
|
a8cdaebab1 | ||
|
|
22393dd172 | ||
|
|
a6fdb31f1e | ||
|
|
dd15d4fc65 | ||
|
|
edb1abf82b | ||
|
|
0b6411661e | ||
|
|
604a80e6cb | ||
|
|
fe6a929f1e | ||
|
|
2267e728cf | ||
|
|
abc8a02b72 | ||
|
|
2270887faf | ||
|
|
bad4686506 | ||
|
|
818caad7cb | ||
|
|
672b7e893e | ||
|
|
a8e66d84f4 | ||
|
|
e9090d64b9 | ||
|
|
728a205b7b | ||
|
|
67ba29d45a | ||
|
|
758caffdf9 | ||
|
|
302227e8a9 | ||
|
|
04a31220fb | ||
|
|
d44a23af0c | ||
|
|
18a79589c2 | ||
|
|
b7727b0e10 | ||
|
|
5edc40fed5 | ||
|
|
be0d0b3ca1 | ||
|
|
ef8ebb0098 | ||
|
|
2fa61e1377 | ||
|
|
b23e37f38e | ||
|
|
cf45e32702 | ||
|
|
b5965c6c37 | ||
|
|
e39cc45d23 | ||
|
|
f0434cb12c | ||
|
|
c87caaed2d | ||
|
|
04af7ecf53 |
@@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
## The Goal for Rocket
|
## The Goal for Rocket
|
||||||
|
|
||||||
> Our goal is to provide developers with a meta framework for static websites with a spricle of JavaScript.
|
> Our goal is to provide developers with a meta framework for static websites with a sprinkle of JavaScript.
|
||||||
|
|
||||||
Get a site up and running in no time and focus on the content.
|
Get a site up and running in no time and focus on the content.
|
||||||
You can still tweak every detail of every underlying tool that gets used.
|
You can still tweak every detail of every underlying tool that gets used.
|
||||||
@@ -54,7 +54,7 @@ Rocket is part of the [Modern Web Family](https://twitter.com/modern_web_dev).
|
|||||||
|
|
||||||
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/main/CONTRIBUTING.md). Also, feel free to drop into [slack](https://rocket.modern-web.dev/discover/slack/) 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 [slack](https://rocket.modern-web.dev/about/slack/) and say hi. 👋
|
||||||
|
|
||||||
### Financial Contributors
|
### Financial Contributors
|
||||||
|
|
||||||
|
|||||||
10
docs/_assets/body.css
Normal file
10
docs/_assets/body.css
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
html {
|
||||||
|
--demo-background-color: #eee;
|
||||||
|
--demo-color: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[theme="dark"] body {
|
||||||
|
background: #333;
|
||||||
|
--demo-background-color: #888;
|
||||||
|
--demo-color: #eee;
|
||||||
|
}
|
||||||
@@ -7,3 +7,5 @@
|
|||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
<meta name="twitter:creator" content="@modern_web_dev" />
|
<meta name="twitter:creator" content="@modern_web_dev" />
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="{{ '/_assets/body.css' | asset | url }}" mdjs-use>
|
||||||
|
|||||||
45
docs/docs/configuration/service-worker.md
Normal file
45
docs/docs/configuration/service-worker.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# Configuration >> Service Worker ||30
|
||||||
|
|
||||||
|
Rocket does come with a default service worker that will
|
||||||
|
|
||||||
|
- cache already visited pages
|
||||||
|
- cache assets of visited pages (up to 100 files then it replaces older entries)
|
||||||
|
- reload the page if a newer html page version is available on service worker activation
|
||||||
|
|
||||||
|
## Adjusting the file name
|
||||||
|
|
||||||
|
Changing the service worker file name can be quite a hassle so you can adjust generate file name via a config.
|
||||||
|
|
||||||
|
👉 `rocket.config.js`
|
||||||
|
|
||||||
|
```js
|
||||||
|
export default {
|
||||||
|
serviceWorkerName: 'my-service-worker-name.js',
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Meet the Service Worker
|
||||||
|
|
||||||
|
The default service worker will work for many scenarios however your needs my vary.
|
||||||
|
To enable different service worker strategies you can replace the default service worker code by providing a file at `_assets/service-worker.js`.
|
||||||
|
This file will be auto transformed and generated in the root of the website using the defined `serviceWorkerName`.
|
||||||
|
|
||||||
|
For inspiration, you can take a look at the default config.
|
||||||
|
|
||||||
|
[https://github.com/modernweb-dev/rocket/blob/main/packages/cli/preset/\_assets/service-worker.js](https://github.com/modernweb-dev/rocket/blob/main/packages/cli/preset/_assets/service-worker.js)
|
||||||
|
|
||||||
|
Be sure to check out [workbox](https://developers.google.com/web/tools/workbox) for more service worker magic.
|
||||||
|
|
||||||
|
And if you wanna have a 30 minutes crash course we highly recommend the talk [Service Workers For The Rest Of Us](https://vimeo.com/362260166) by [Philip Walton](https://twitter.com/philwalton).
|
||||||
|
|
||||||
|
## Registration
|
||||||
|
|
||||||
|
The registration happens via another file that you can also overwrite at `_assets/scripts/registerServiceWorker.js`.
|
||||||
|
|
||||||
|
Below you find the default implementation.
|
||||||
|
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
```js
|
||||||
|
{{ '/_assets/scripts/registerServiceWorker.js' | asset | toAbsPath | inlineFilePath; }}
|
||||||
|
```
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
69
docs/docs/markdown-javascript/assets/demo-element.js
Normal file
69
docs/docs/markdown-javascript/assets/demo-element.js
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
class DemoElement extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.attachShadow({ mode: 'open' });
|
||||||
|
|
||||||
|
this.platform = 'the web';
|
||||||
|
this.language = 'en-US';
|
||||||
|
this.theme = 'light';
|
||||||
|
|
||||||
|
this.observer = new MutationObserver(this.updateData);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateData = () => {
|
||||||
|
this.platform = document.documentElement.getAttribute('platform') || 'the web';
|
||||||
|
this.language = document.documentElement.getAttribute('data-lang') || 'en-US';
|
||||||
|
this.theme = document.documentElement.getAttribute('theme') || 'light';
|
||||||
|
this.requestUpdate();
|
||||||
|
};
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.updateData();
|
||||||
|
|
||||||
|
this.observer.observe(document.documentElement, { attributes: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
requestUpdate() {
|
||||||
|
this.shadowRoot.innerHTML = this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return `
|
||||||
|
<style>
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
background: var(--demo-background-color);
|
||||||
|
color: var(--demo-color);
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host[platform~="web"] {
|
||||||
|
border-bottom: 2px solid #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 640px) {
|
||||||
|
.about {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.about ul {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<p>Hello I am DemoElement 👋</p>
|
||||||
|
<div class="about">
|
||||||
|
<ul>
|
||||||
|
<li>My purpose is to demonstrate how an element can adopt to different environments</li>
|
||||||
|
<li>I like <strong>${this.platform}</strong></li>
|
||||||
|
</ul>
|
||||||
|
<ul>
|
||||||
|
<li>My mother languages is <strong>${this.language}</strong></li>
|
||||||
|
<li>I feel very comfortable in the <strong>${this.theme}</strong></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('demo-element', DemoElement);
|
||||||
@@ -56,8 +56,8 @@ mdjs comes with some additional helpers you can choose to import:
|
|||||||
|
|
||||||
````md
|
````md
|
||||||
```js script
|
```js script
|
||||||
import '@mdjs/mdjs-story/mdjs-story.js';
|
import '@mdjs/mdjs-story/define';
|
||||||
import '@mdjs/mdjs-preview/mdjs-preview.js';
|
import '@mdjs/mdjs-preview/define';
|
||||||
```
|
```
|
||||||
````
|
````
|
||||||
|
|
||||||
@@ -65,8 +65,8 @@ Once loaded you can use them like so:
|
|||||||
|
|
||||||
````md
|
````md
|
||||||
```js script
|
```js script
|
||||||
import '@mdjs/mdjs-story/mdjs-story.js';
|
import '@mdjs/mdjs-story/define';
|
||||||
import '@mdjs/mdjs-preview/mdjs-preview.js';
|
import '@mdjs/mdjs-preview/define';
|
||||||
```
|
```
|
||||||
````
|
````
|
||||||
|
|
||||||
@@ -118,8 +118,8 @@ export const JsPreviewStory = () => html` <demo-wc-card>JS Preview Story</demo-w
|
|||||||
Here is a live example from [demo-wc-card](https://www.npmjs.com/package/demo-wc-card).
|
Here is a live example from [demo-wc-card](https://www.npmjs.com/package/demo-wc-card).
|
||||||
|
|
||||||
```js script
|
```js script
|
||||||
import '@mdjs/mdjs-story/mdjs-story.js';
|
import '@mdjs/mdjs-story/define';
|
||||||
import '@mdjs/mdjs-preview/mdjs-preview.js';
|
import '@mdjs/mdjs-preview/define';
|
||||||
import { html } from 'lit-html';
|
import { html } from 'lit-html';
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -132,31 +132,6 @@ export const header = () => {
|
|||||||
|
|
||||||
## Supported Systems
|
## Supported Systems
|
||||||
|
|
||||||
### es-dev-server
|
|
||||||
|
|
||||||
Preview your mdjs readme with live demos and auto reload.
|
|
||||||
|
|
||||||
- Add to your `package.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
"scripts": {
|
|
||||||
"start": "es-dev-server",
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- Create a `es-dev-server.config.js` in the root of your repository.
|
|
||||||
|
|
||||||
```js
|
|
||||||
const { mdjsTransformer } = require('@mdjs/core');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
nodeResolve: true,
|
|
||||||
open: 'README.md',
|
|
||||||
watch: true,
|
|
||||||
responseTransformers: [mdjsTransformer],
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### Storybook
|
### Storybook
|
||||||
|
|
||||||
Please check out [@open-wc/demoing-storybook](https://open-wc.org/demoing/) for a fully integrated setup.
|
Please check out [@open-wc/demoing-storybook](https://open-wc.org/demoing/) for a fully integrated setup.
|
||||||
|
|||||||
@@ -2,18 +2,165 @@
|
|||||||
|
|
||||||
You can showcase live running code by annotating a code block with `js preview-story`.
|
You can showcase live running code by annotating a code block with `js preview-story`.
|
||||||
|
|
||||||
````md
|
## Features
|
||||||
```js preview-story
|
|
||||||
import { html } from 'lit-html';
|
|
||||||
|
|
||||||
export const foo = () => html` <p>my html</p> `;
|
- Shows components inside the page as they are
|
||||||
|
- You can enable “Simulation Mode” to break them out
|
||||||
|
- Simulation mode renders in an iframe to supporting media queries and isolated Simulation settings
|
||||||
|
- Simulation Settings
|
||||||
|
- Style (windows, mac, android, iOS)
|
||||||
|
- Size (small, medium, large, Galaxy S5, iPhone X, iPad …)
|
||||||
|
- Automatic Height
|
||||||
|
- Theme (light, dark)
|
||||||
|
- Language (en, nl, …)
|
||||||
|
- Settings are ”global” for all Simulators (e.g. changing one will change all)
|
||||||
|
- Settings can be remembered for other pages / return visits
|
||||||
|
|
||||||
|
```js script
|
||||||
|
import { html } from 'lit-html';
|
||||||
|
import './assets/demo-element.js';
|
||||||
|
```
|
||||||
|
|
||||||
|
## JavaScript Story
|
||||||
|
|
||||||
|
````md
|
||||||
|
```js script
|
||||||
|
import { html } from 'lit-html';
|
||||||
|
import './assets/demo-element.js';
|
||||||
|
```
|
||||||
|
|
||||||
|
```js preview-story
|
||||||
|
export const foo = () => html`<demo-element></demo-element>`;
|
||||||
```
|
```
|
||||||
````
|
````
|
||||||
|
|
||||||
will result in
|
will result in
|
||||||
|
|
||||||
```js preview-story
|
```js preview-story
|
||||||
import { html } from 'lit-html';
|
export const foo = () => html` <demo-element></demo-element> `;
|
||||||
|
```
|
||||||
export const foo = () => html` <p>my html</p> `;
|
|
||||||
|
## HTML Story
|
||||||
|
|
||||||
|
````md
|
||||||
|
```html preview-story
|
||||||
|
<demo-element></demo-element>
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
will result in
|
||||||
|
|
||||||
|
```html preview-story
|
||||||
|
<demo-element></demo-element>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setup Simulation Mode
|
||||||
|
|
||||||
|
For simulation mode we need a dedicated html file that will be used as an iframe target while loading stories.
|
||||||
|
|
||||||
|
The fastest way to create such a file is to use the `layout-simulator` layout.
|
||||||
|
|
||||||
|
Create a file `docs/simulator.md` with the following content.
|
||||||
|
|
||||||
|
```md
|
||||||
|
---
|
||||||
|
layout: layout-simulator
|
||||||
|
eleventyExcludeFromCollections: true
|
||||||
|
excludeFromSearch: true
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
Once you have that you need to configure it for the story renderer by setting it in your `rocket.config.js`.
|
||||||
|
|
||||||
|
```js
|
||||||
|
export default {
|
||||||
|
setupUnifiedPlugins: [
|
||||||
|
adjustPluginOptions('mdjsSetupCode', {
|
||||||
|
simulationSettings: { simulatorUrl: '/simulator/' },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
<inline-notification type="tip">
|
||||||
|
|
||||||
|
You can freely choose the path for the "simulator" by creating the md file in a different folder and adjusting the path in the config.
|
||||||
|
|
||||||
|
</inline-notification>
|
||||||
|
|
||||||
|
## Simulator states
|
||||||
|
|
||||||
|
To simulate these stats that usually come from the device itself we put those infos on the document tag.
|
||||||
|
|
||||||
|
We can simulate the following settings
|
||||||
|
|
||||||
|
1. `platform`
|
||||||
|
Adopting styles and behavior depending on which device platform you are.
|
||||||
|
```html
|
||||||
|
<html platform="web"></html>
|
||||||
|
<html platform="android"></html>
|
||||||
|
<html platform="ios"></html>
|
||||||
|
<!-- potentially later -->
|
||||||
|
<html platform="web-windows"></html>
|
||||||
|
<html platform="web-mac"></html>
|
||||||
|
```
|
||||||
|
2. `theme`
|
||||||
|
Adjust your styles based on a theme - light/dark are the default but you can add as many as you want.
|
||||||
|
```html
|
||||||
|
<html theme="light"></html>
|
||||||
|
<html theme="dark"></html>
|
||||||
|
```
|
||||||
|
3. `language`
|
||||||
|
Best to relay on `data-lang` as `lang` often gets changes by translations services which may interfere with your translation loading system.
|
||||||
|
```html
|
||||||
|
<html lang="en-US" data-lang="en-US"></html>
|
||||||
|
<html lang="de-DE" data-lang="de-DE"></html>
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to react to such document changes you can use an [MutationObserver](https://developer.mozilla.org/de/docs/Web/API/MutationObserver).
|
||||||
|
|
||||||
|
For a vanilla web component it could look something like this:
|
||||||
|
|
||||||
|
```js
|
||||||
|
class DemoElement extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.attachShadow({ mode: 'open' });
|
||||||
|
|
||||||
|
this.platform = 'the web';
|
||||||
|
this.language = 'en-US';
|
||||||
|
this.theme = 'light';
|
||||||
|
|
||||||
|
this.observer = new MutationObserver(this.updateData);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateData = () => {
|
||||||
|
this.platform = document.documentElement.getAttribute('platform') || 'the web';
|
||||||
|
this.language = document.documentElement.getAttribute('data-lang') || 'en-US';
|
||||||
|
this.theme = document.documentElement.getAttribute('theme') || 'light';
|
||||||
|
this.requestUpdate();
|
||||||
|
};
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.updateData();
|
||||||
|
|
||||||
|
this.observer.observe(document.documentElement, { attributes: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
requestUpdate() {
|
||||||
|
this.shadowRoot.innerHTML = this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return `
|
||||||
|
...
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('demo-element', DemoElement);
|
||||||
|
```
|
||||||
|
|
||||||
|
```js script
|
||||||
|
import '@rocket/launch/inline-notification/inline-notification.js';
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -2,10 +2,16 @@
|
|||||||
|
|
||||||
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`.
|
||||||
|
|
||||||
````md
|
```js script
|
||||||
```js story
|
|
||||||
import { html } from 'lit-html';
|
import { html } from 'lit-html';
|
||||||
|
```
|
||||||
|
|
||||||
|
````md
|
||||||
|
```js script
|
||||||
|
import { html } from 'lit-html';
|
||||||
|
```
|
||||||
|
|
||||||
|
```js story
|
||||||
export const foo = () => html` <p>my html</p> `;
|
export const foo = () => html` <p>my html</p> `;
|
||||||
```
|
```
|
||||||
````
|
````
|
||||||
@@ -13,7 +19,5 @@ export const foo = () => html` <p>my html</p> `;
|
|||||||
will result in
|
will result in
|
||||||
|
|
||||||
```js story
|
```js story
|
||||||
import { html } from 'lit-html';
|
|
||||||
|
|
||||||
export const foo = () => html` <p>my html</p> `;
|
export const foo = () => html` <p>my html</p> `;
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ You write modern JavaScript using the latest browser features. Rollup will optim
|
|||||||
- Set HTML or JavaScript as input and/or output
|
- Set HTML or JavaScript as input and/or output
|
||||||
- Optimized for browsers which support modules
|
- Optimized for browsers which support modules
|
||||||
- Loads polyfills using feature detection
|
- Loads polyfills using feature detection
|
||||||
- Generates a service worker
|
|
||||||
- Minifies JavaScript
|
- Minifies JavaScript
|
||||||
- Minifies lit-html templates
|
- Minifies lit-html templates
|
||||||
|
|
||||||
|
|||||||
@@ -47,3 +47,9 @@ export const headlineConverter = () => html`
|
|||||||
```
|
```
|
||||||
|
|
||||||
How it then works is very similar to https://www.11ty.dev/docs/plugins/navigation/
|
How it then works is very similar to https://www.11ty.dev/docs/plugins/navigation/
|
||||||
|
|
||||||
|
## Sidebar redirects
|
||||||
|
|
||||||
|
By default, the sidebar nav redirects clicks on category headings to the first child page in that category.
|
||||||
|
|
||||||
|
To disable those redirects, override `_includes/_joiningBlocks/_layoutSidebar/sidebar/20-navigation.njk` and add the `no-redirects` attribute to the `<rocket-navigation>` element.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ For that reason Rocket creates those automatically with the title, parent title,
|
|||||||
|
|
||||||
It will look like this but with your logo:
|
It will look like this but with your logo:
|
||||||
|
|
||||||
<img src="{{ socialMediaImage }}" width="1200" height="630" alt="Social Media Image of this page" style="border: 1px solid #000" />
|
<img src="{{ socialMediaImage | url }}" width="1200" height="630" alt="Social Media Image of this page" style="border: 1px solid #000" />
|
||||||
|
|
||||||
There are multiple ways you can modify it.
|
There are multiple ways you can modify it.
|
||||||
|
|
||||||
|
|||||||
5
docs/simulator.md
Normal file
5
docs/simulator.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
layout: layout-simulator
|
||||||
|
eleventyExcludeFromCollections: true
|
||||||
|
excludeFromSearch: true
|
||||||
|
---
|
||||||
@@ -38,6 +38,6 @@
|
|||||||
"testing"
|
"testing"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"plugins-manager": "^0.2.0"
|
"plugins-manager": "^0.2.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,23 @@
|
|||||||
# @rocket/building-rollup
|
# @rocket/building-rollup
|
||||||
|
|
||||||
|
## 0.3.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- 2724f07: Stop auto generating a service worker from a template. Setup your own and then bundle via `createServiceWorkerConfig`.
|
||||||
|
|
||||||
|
## 0.2.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- bad4686: Preserve attributes on script tags. Preserve export names.
|
||||||
|
|
||||||
|
## 0.1.3
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- be0d0b3: fix: add missing main entry to the packages
|
||||||
|
|
||||||
## 0.1.2
|
## 0.1.2
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -6,3 +6,7 @@
|
|||||||
export { createBasicConfig, createBasicMetaConfig } from './src/createBasicConfig.js';
|
export { createBasicConfig, createBasicMetaConfig } from './src/createBasicConfig.js';
|
||||||
export { createSpaConfig, createSpaMetaConfig } from './src/createSpaConfig.js';
|
export { createSpaConfig, createSpaMetaConfig } from './src/createSpaConfig.js';
|
||||||
export { createMpaConfig, createMpaMetaConfig } from './src/createMpaConfig.js';
|
export { createMpaConfig, createMpaMetaConfig } from './src/createMpaConfig.js';
|
||||||
|
export {
|
||||||
|
createServiceWorkerConfig,
|
||||||
|
createServiceWorkerMetaConfig,
|
||||||
|
} from './src/createServiceWorkerConfig.js';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@rocket/building-rollup",
|
"name": "@rocket/building-rollup",
|
||||||
"version": "0.1.2",
|
"version": "0.3.0",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
},
|
},
|
||||||
"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/tools/building-rollup/",
|
"homepage": "https://rocket.modern-web.dev/docs/tools/building-rollup/",
|
||||||
|
"main": "./index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./index.js"
|
".": "./index.js"
|
||||||
@@ -54,11 +55,16 @@
|
|||||||
"@babel/preset-env": "^7.12.11",
|
"@babel/preset-env": "^7.12.11",
|
||||||
"@rollup/plugin-babel": "^5.2.2",
|
"@rollup/plugin-babel": "^5.2.2",
|
||||||
"@rollup/plugin-node-resolve": "^11.0.1",
|
"@rollup/plugin-node-resolve": "^11.0.1",
|
||||||
"@web/rollup-plugin-html": "^1.4.0",
|
"@rollup/plugin-replace": "^2.4.2",
|
||||||
|
"@web/rollup-plugin-html": "^1.6.0",
|
||||||
"@web/rollup-plugin-import-meta-assets": "^1.0.4",
|
"@web/rollup-plugin-import-meta-assets": "^1.0.4",
|
||||||
"@web/rollup-plugin-polyfills-loader": "^1.0.3",
|
"@web/rollup-plugin-polyfills-loader": "^1.1.0",
|
||||||
"browserslist": "^4.16.1",
|
"browserslist": "^4.16.1",
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
"rollup-plugin-workbox": "^6.1.0"
|
"workbox-broadcast-update": "^6.1.5",
|
||||||
|
"workbox-cacheable-response": "^6.1.5",
|
||||||
|
"workbox-expiration": "^6.1.5",
|
||||||
|
"workbox-routing": "^6.1.5",
|
||||||
|
"workbox-strategies": "^6.1.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export function createBasicMetaConfig(userConfig = { output: {} }) {
|
|||||||
const assetName = `[${developmentMode ? 'name' : 'hash'}][extname]`;
|
const assetName = `[${developmentMode ? 'name' : 'hash'}][extname]`;
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
preserveEntrySignatures: false,
|
preserveEntrySignatures: 'strict',
|
||||||
treeshake: !developmentMode,
|
treeshake: !developmentMode,
|
||||||
setupPlugins: [],
|
setupPlugins: [],
|
||||||
...userConfig,
|
...userConfig,
|
||||||
|
|||||||
@@ -15,10 +15,6 @@ export function createMpaMetaConfig(userConfig = { output: {}, setupPlugins: []
|
|||||||
adjustPluginOptions('html', {
|
adjustPluginOptions('html', {
|
||||||
flattenOutput: false,
|
flattenOutput: false,
|
||||||
}),
|
}),
|
||||||
adjustPluginOptions('workbox', config => {
|
|
||||||
delete config.navigateFallback;
|
|
||||||
return config;
|
|
||||||
}),
|
|
||||||
...config.setupPlugins,
|
...config.setupPlugins,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
91
packages/building-rollup/src/createServiceWorkerConfig.js
Normal file
91
packages/building-rollup/src/createServiceWorkerConfig.js
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import resolve from '@rollup/plugin-node-resolve';
|
||||||
|
import { terser } from 'rollup-plugin-terser';
|
||||||
|
import babelPkg from '@rollup/plugin-babel';
|
||||||
|
import replace from '@rollup/plugin-replace';
|
||||||
|
|
||||||
|
import { metaConfigToRollupConfig } from 'plugins-manager';
|
||||||
|
|
||||||
|
const { babel } = babelPkg;
|
||||||
|
|
||||||
|
export function createServiceWorkerConfig(userConfig) {
|
||||||
|
const { config, metaPlugins } = createServiceWorkerMetaConfig(userConfig);
|
||||||
|
return metaConfigToRollupConfig(config, metaPlugins);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createServiceWorkerMetaConfig(userConfig = { output: {} }) {
|
||||||
|
const developmentMode =
|
||||||
|
typeof userConfig.developmentMode !== undefined
|
||||||
|
? userConfig.developmentMode
|
||||||
|
: !!process.env.ROLLUP_WATCH;
|
||||||
|
delete userConfig.developmentMode;
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
treeshake: !developmentMode,
|
||||||
|
setupPlugins: [],
|
||||||
|
...userConfig,
|
||||||
|
|
||||||
|
output: {
|
||||||
|
format: 'iife',
|
||||||
|
file: 'service-worker.js',
|
||||||
|
...userConfig.output,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let metaPlugins = [
|
||||||
|
{
|
||||||
|
name: 'node-resolve',
|
||||||
|
plugin: resolve,
|
||||||
|
options: {
|
||||||
|
moduleDirectories: ['node_modules', 'web_modules'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'replace',
|
||||||
|
plugin: replace,
|
||||||
|
options: {
|
||||||
|
'process.env.NODE_ENV': JSON.stringify(developmentMode ? 'development' : 'production'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'babel',
|
||||||
|
plugin: babel,
|
||||||
|
options: {
|
||||||
|
babelHelpers: 'bundled',
|
||||||
|
compact: true,
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
'@babel/preset-env',
|
||||||
|
{
|
||||||
|
targets: [
|
||||||
|
'last 3 Chrome major versions',
|
||||||
|
'last 3 ChromeAndroid major versions',
|
||||||
|
'last 3 Firefox major versions',
|
||||||
|
'last 3 Edge major versions',
|
||||||
|
'last 3 Safari major versions',
|
||||||
|
'last 3 iOS major versions',
|
||||||
|
],
|
||||||
|
useBuiltIns: false,
|
||||||
|
shippedProposals: true,
|
||||||
|
modules: false,
|
||||||
|
bugfixes: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'terser',
|
||||||
|
plugin: terser,
|
||||||
|
options: {
|
||||||
|
mangle: {
|
||||||
|
toplevel: true,
|
||||||
|
properties: {
|
||||||
|
regex: /(^_|_$)/,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return { config, metaPlugins, developmentMode };
|
||||||
|
}
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
import path from 'path';
|
|
||||||
import { rollupPluginHTML } from '@web/rollup-plugin-html';
|
import { rollupPluginHTML } from '@web/rollup-plugin-html';
|
||||||
import { generateSW } from 'rollup-plugin-workbox';
|
|
||||||
import { importMetaAssets } from '@web/rollup-plugin-import-meta-assets';
|
import { importMetaAssets } from '@web/rollup-plugin-import-meta-assets';
|
||||||
import { polyfillsLoader } from '@web/rollup-plugin-polyfills-loader';
|
import { polyfillsLoader } from '@web/rollup-plugin-polyfills-loader';
|
||||||
import { metaConfigToRollupConfig } from 'plugins-manager';
|
import { metaConfigToRollupConfig } from 'plugins-manager';
|
||||||
@@ -37,31 +35,6 @@ export function createSpaMetaConfig(userConfig = { output: {} }) {
|
|||||||
options: {
|
options: {
|
||||||
rootDir,
|
rootDir,
|
||||||
absoluteBaseUrl,
|
absoluteBaseUrl,
|
||||||
injectServiceWorker: true,
|
|
||||||
serviceWorkerPath: path.join(config.output.dir, 'service-worker.js'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'workbox',
|
|
||||||
plugin: generateSW,
|
|
||||||
options: {
|
|
||||||
// Keep 'legacy-*.js' just for retro compatibility
|
|
||||||
globIgnores: ['polyfills/*.js', 'legacy-*.js', 'nomodule-*.js'],
|
|
||||||
navigateFallback: '/index.html',
|
|
||||||
// where to output the generated sw
|
|
||||||
swDest: path.join(config.output.dir, 'service-worker.js'),
|
|
||||||
// directory to match patterns against to be precached
|
|
||||||
globDirectory: path.join(config.output.dir),
|
|
||||||
// cache any html js and css by default
|
|
||||||
globPatterns: ['**/*.{html,js,css,webmanifest}'],
|
|
||||||
skipWaiting: true,
|
|
||||||
clientsClaim: true,
|
|
||||||
runtimeCaching: [
|
|
||||||
{
|
|
||||||
urlPattern: 'polyfills/*.js',
|
|
||||||
handler: 'CacheFirst',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ describe('plugin count', () => {
|
|||||||
expect(config.plugins.length).to.equal(3);
|
expect(config.plugins.length).to.equal(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('createSpaConfig has 7 plugins', () => {
|
it('createSpaConfig has 6 plugins', () => {
|
||||||
const config = createSpaConfig();
|
const config = createSpaConfig();
|
||||||
expect(config.plugins.length).to.equal(7);
|
expect(config.plugins.length).to.equal(6);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('createMpaConfig has 7 plugins', () => {
|
it('createMpaConfig has 6 plugins', () => {
|
||||||
const config = createMpaConfig();
|
const config = createMpaConfig();
|
||||||
expect(config.plugins.length).to.equal(7);
|
expect(config.plugins.length).to.equal(6);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,10 +26,7 @@ async function execute(configString) {
|
|||||||
const config = (await import(configPath)).default;
|
const config = (await import(configPath)).default;
|
||||||
await buildAndWrite(config);
|
await buildAndWrite(config);
|
||||||
|
|
||||||
return async (
|
return async (fileName, { stripToBody = false, stripStartEndWhitespace = true } = {}) => {
|
||||||
fileName,
|
|
||||||
{ stripServiceWorker = false, stripToBody = false, stripStartEndWhitespace = true } = {},
|
|
||||||
) => {
|
|
||||||
let text = await fs.promises.readFile(
|
let text = await fs.promises.readFile(
|
||||||
path.join(config.output.dir, fileName.split('/').join(path.sep)),
|
path.join(config.output.dir, fileName.split('/').join(path.sep)),
|
||||||
);
|
);
|
||||||
@@ -39,11 +36,6 @@ async function execute(configString) {
|
|||||||
const bodyCloseTagStart = text.indexOf('</body>');
|
const bodyCloseTagStart = text.indexOf('</body>');
|
||||||
text = text.substring(bodyOpenTagEnd, bodyCloseTagStart);
|
text = text.substring(bodyOpenTagEnd, bodyCloseTagStart);
|
||||||
}
|
}
|
||||||
if (stripServiceWorker) {
|
|
||||||
const scriptOpenTagEnd = text.indexOf('<script inject-service-worker');
|
|
||||||
const scriptCloseTagStart = text.indexOf('</script>', scriptOpenTagEnd) + 9;
|
|
||||||
text = text.substring(0, scriptOpenTagEnd) + text.substring(scriptCloseTagStart);
|
|
||||||
}
|
|
||||||
if (stripStartEndWhitespace) {
|
if (stripStartEndWhitespace) {
|
||||||
text = text.trim();
|
text = text.trim();
|
||||||
}
|
}
|
||||||
@@ -57,25 +49,26 @@ describe('createMapConfig', () => {
|
|||||||
|
|
||||||
const indexHtml = await readOutput('index.html', {
|
const indexHtml = await readOutput('index.html', {
|
||||||
stripToBody: true,
|
stripToBody: true,
|
||||||
stripServiceWorker: true,
|
|
||||||
});
|
});
|
||||||
expect(indexHtml).to.equal('<h1>Only static HTML content in index.html</h1>');
|
expect(indexHtml).to.equal('<h1>Only static HTML content in index.html</h1>');
|
||||||
|
|
||||||
const subHtmlIndexHtml = await readOutput('sub-html/index.html', {
|
const subHtmlIndexHtml = await readOutput('sub-html/index.html', {
|
||||||
stripToBody: true,
|
stripToBody: true,
|
||||||
stripServiceWorker: true,
|
|
||||||
});
|
});
|
||||||
expect(subHtmlIndexHtml).to.equal('<h1>Only static HTML content in sub-html/index.html</h1>');
|
expect(subHtmlIndexHtml).to.equal('<h1>Only static HTML content in sub-html/index.html</h1>');
|
||||||
|
|
||||||
const subJsIndexHtml = await readOutput('sub-js/index.html', {
|
const subJsIndexHtml = await readOutput('sub-js/index.html', {
|
||||||
stripToBody: true,
|
stripToBody: true,
|
||||||
stripServiceWorker: true,
|
|
||||||
});
|
});
|
||||||
expect(subJsIndexHtml).to.equal(
|
expect(subJsIndexHtml).to.equal(
|
||||||
'<h1>Has js in sub-js/index.html</h1>\n\n\n<script type="module" src="../sub-js.js"></script>',
|
'<h1>Has js in sub-js/index.html</h1>\n\n\n<script type="module" src="../sub-js.js"></script>',
|
||||||
);
|
);
|
||||||
|
|
||||||
const serviceWorkerJs = await readOutput('service-worker.js');
|
const subJsAbsoluteIndexHtml = await readOutput('sub-js-absolute/index.html', {
|
||||||
expect(serviceWorkerJs).to.include('Promise'); // not empty string might be enough...
|
stripToBody: true,
|
||||||
|
});
|
||||||
|
expect(subJsAbsoluteIndexHtml).to.equal(
|
||||||
|
'<h1>Has js in sub-js-absolute/index.html</h1>\n\n\n<script type="module" src="../sub-js-absolute.js"></script>',
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<h1>Has js in sub-js-absolute/index.html</h1>
|
||||||
|
<script type="module" src="/sub-js-absolute/sub-js-absolute.js"></script>
|
||||||
@@ -1,5 +1,17 @@
|
|||||||
# check-html-links
|
# check-html-links
|
||||||
|
|
||||||
|
## 0.2.2
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 66c2d78: fix: windows path issue
|
||||||
|
|
||||||
|
## 0.2.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- be0d0b3: fix: add missing main entry to the packages
|
||||||
|
|
||||||
## 0.2.0
|
## 0.2.0
|
||||||
|
|
||||||
### Minor Changes
|
### Minor Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "check-html-links",
|
"name": "check-html-links",
|
||||||
"version": "0.2.0",
|
"version": "0.2.2",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
},
|
},
|
||||||
"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/tools/check-html-links/",
|
"homepage": "https://rocket.modern-web.dev/docs/tools/check-html-links/",
|
||||||
|
"main": "./index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
"check-html-links": "src/cli.js"
|
"check-html-links": "src/cli.js"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { createRequire } from 'module';
|
|||||||
|
|
||||||
import { listFiles } from './listFiles.js';
|
import { listFiles } from './listFiles.js';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import slash from 'slash';
|
||||||
|
|
||||||
/** @typedef {import('../types/main').Link} Link */
|
/** @typedef {import('../types/main').Link} Link */
|
||||||
/** @typedef {import('../types/main').LocalFile} LocalFile */
|
/** @typedef {import('../types/main').LocalFile} LocalFile */
|
||||||
@@ -45,7 +46,7 @@ function extractReferences(htmlFilePath) {
|
|||||||
if (ev === SaxEventType.Attribute) {
|
if (ev === SaxEventType.Attribute) {
|
||||||
const data = /** @type {Attribute} */ (/** @type {any} */ (_data));
|
const data = /** @type {Attribute} */ (/** @type {any} */ (_data));
|
||||||
const attributeName = data.name.toString();
|
const attributeName = data.name.toString();
|
||||||
const value = data.value.toString();
|
const value = slash(data.value.toString());
|
||||||
const entry = {
|
const entry = {
|
||||||
attribute: attributeName,
|
attribute: attributeName,
|
||||||
value,
|
value,
|
||||||
|
|||||||
@@ -1,5 +1,68 @@
|
|||||||
# @rocket/cli
|
# @rocket/cli
|
||||||
|
|
||||||
|
## 0.7.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- 2724f07: The service worker no longer precaches all urls and assets. It now
|
||||||
|
|
||||||
|
- caches already visited pages
|
||||||
|
- caches assets of visited pages (up to 100 files then it replaces older entries)
|
||||||
|
- on service worker activation it will reload the page if a newer version is available
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [2724f07]
|
||||||
|
- @rocket/building-rollup@0.3.0
|
||||||
|
|
||||||
|
## 0.6.3
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 2b7f1ee: Add support for pathprefix
|
||||||
|
- Updated dependencies [2b7f1ee]
|
||||||
|
- @rocket/eleventy-plugin-mdjs-unified@0.4.1
|
||||||
|
|
||||||
|
## 0.6.2
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- ed86ff2: Do not set `data-localize-lang` in the simulator iframe html tag
|
||||||
|
- f4a0ab5: Pass document shadowRoot to iframe
|
||||||
|
- c675820: fix: windows path issue avoid filtering of index section of collections
|
||||||
|
|
||||||
|
## 0.6.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- abc8a02: You can now use an absolute path for the rootDir
|
||||||
|
- Updated dependencies [bad4686]
|
||||||
|
- Updated dependencies [2267e72]
|
||||||
|
- @rocket/building-rollup@0.2.0
|
||||||
|
- @rocket/eleventy-plugin-mdjs-unified@0.4.0
|
||||||
|
|
||||||
|
## 0.6.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- 5edc40f: Make sure each rocket instance has it's own eleventy config'
|
||||||
|
- ef8ebb0: To support dynamically created content to be part of the anchor navigation of the page we now analyze the final html output instead of `entry.templateContent`.
|
||||||
|
|
||||||
|
BREAKING CHANGE:
|
||||||
|
|
||||||
|
- only add anchors for the currently active pages (before it added anchor for every page)
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- be0d0b3: fix: add missing main entry to the packages
|
||||||
|
- Updated dependencies [be0d0b3]
|
||||||
|
- Updated dependencies [ef8ebb0]
|
||||||
|
- @rocket/building-rollup@0.1.3
|
||||||
|
- check-html-links@0.2.1
|
||||||
|
- @rocket/core@0.1.2
|
||||||
|
- plugins-manager@0.2.1
|
||||||
|
- @rocket/eleventy-rocket-nav@0.3.0
|
||||||
|
|
||||||
## 0.5.2
|
## 0.5.2
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@rocket/cli",
|
"name": "@rocket/cli",
|
||||||
"version": "0.5.2",
|
"version": "0.7.0",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
},
|
},
|
||||||
"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/",
|
"homepage": "https://rocket.modern-web.dev/",
|
||||||
|
"main": "./index.cjs",
|
||||||
"bin": {
|
"bin": {
|
||||||
"rocket": "src/cli.js"
|
"rocket": "src/cli.js"
|
||||||
},
|
},
|
||||||
@@ -56,23 +57,25 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@11ty/eleventy": "^0.11.1",
|
"@11ty/eleventy": "^0.11.1",
|
||||||
"@11ty/eleventy-img": "^0.7.4",
|
"@11ty/eleventy-img": "^0.7.4",
|
||||||
"@rocket/building-rollup": "^0.1.2",
|
"@rocket/building-rollup": "^0.3.0",
|
||||||
"@rocket/core": "^0.1.1",
|
"@rocket/core": "^0.1.2",
|
||||||
"@rocket/eleventy-plugin-mdjs-unified": "^0.3.1",
|
"@rocket/eleventy-plugin-mdjs-unified": "^0.4.1",
|
||||||
"@rocket/eleventy-rocket-nav": "^0.2.1",
|
"@rocket/eleventy-rocket-nav": "^0.3.0",
|
||||||
"@rollup/plugin-babel": "^5.2.2",
|
"@rollup/plugin-babel": "^5.2.2",
|
||||||
"@rollup/plugin-node-resolve": "^11.0.1",
|
"@rollup/plugin-node-resolve": "^11.0.1",
|
||||||
"@web/config-loader": "^0.1.3",
|
"@web/config-loader": "^0.1.3",
|
||||||
"@web/dev-server": "^0.1.4",
|
"@web/dev-server": "^0.1.4",
|
||||||
"@web/dev-server-rollup": "^0.3.2",
|
"@web/dev-server-rollup": "^0.3.2",
|
||||||
"@web/rollup-plugin-copy": "^0.2.0",
|
"@web/rollup-plugin-copy": "^0.2.0",
|
||||||
"check-html-links": "^0.2.0",
|
"check-html-links": "^0.2.2",
|
||||||
"command-line-args": "^5.1.1",
|
"command-line-args": "^5.1.1",
|
||||||
"command-line-usage": "^6.1.1",
|
"command-line-usage": "^6.1.1",
|
||||||
"fs-extra": "^9.0.1",
|
"fs-extra": "^9.0.1",
|
||||||
"micromatch": "^4.0.2",
|
"micromatch": "^4.0.2",
|
||||||
"plugins-manager": "^0.2.0",
|
"plugins-manager": "^0.2.1",
|
||||||
"utf8": "^3.0.0"
|
"slash": "^3.0.0",
|
||||||
|
"utf8": "^3.0.0",
|
||||||
|
"workbox-window": "^6.1.5"
|
||||||
},
|
},
|
||||||
"types": "dist-types/index.d.ts"
|
"types": "dist-types/index.d.ts"
|
||||||
}
|
}
|
||||||
|
|||||||
22
packages/cli/preset/_assets/scripts/registerServiceWorker.js
Normal file
22
packages/cli/preset/_assets/scripts/registerServiceWorker.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
(async () => {
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
const { Workbox } = await import('workbox-window');
|
||||||
|
|
||||||
|
const url = window.__rocketServiceWorkerUrl || '/service-worker.js';
|
||||||
|
const wb = new Workbox(url);
|
||||||
|
wb.addEventListener('message', event => {
|
||||||
|
if (event.data.type === 'CACHE_UPDATED') {
|
||||||
|
const { updatedURL } = event.data.payload;
|
||||||
|
console.log(`Reloading as a newer version of ${updatedURL} became available!`);
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
wb.register()
|
||||||
|
.then(function () {
|
||||||
|
console.log('ServiceWorker registered.');
|
||||||
|
})
|
||||||
|
.catch(function (err) {
|
||||||
|
console.log('ServiceWorker registration failed: ', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
||||||
29
packages/cli/preset/_assets/service-worker.js
Normal file
29
packages/cli/preset/_assets/service-worker.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { registerRoute } from 'workbox-routing';
|
||||||
|
import { CacheFirst, StaleWhileRevalidate } from 'workbox-strategies';
|
||||||
|
import { BroadcastUpdatePlugin } from 'workbox-broadcast-update';
|
||||||
|
import { ExpirationPlugin } from 'workbox-expiration';
|
||||||
|
|
||||||
|
addEventListener('install', () => {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
skipWaiting();
|
||||||
|
});
|
||||||
|
|
||||||
|
// addEventListener('activate', () => {
|
||||||
|
// console.log('activate');
|
||||||
|
// });
|
||||||
|
|
||||||
|
const cacheFirst = new CacheFirst({
|
||||||
|
cacheName: 'assets',
|
||||||
|
plugins: [
|
||||||
|
new ExpirationPlugin({
|
||||||
|
maxEntries: 100,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const staleWhileRevalidate = new StaleWhileRevalidate({
|
||||||
|
cacheName: 'pages',
|
||||||
|
plugins: [new BroadcastUpdatePlugin()],
|
||||||
|
});
|
||||||
|
|
||||||
|
registerRoute(/(\/|\.html)$/, staleWhileRevalidate);
|
||||||
|
registerRoute(/\.(css|m?js|svg|woff2|png|jpg|gif|json|xml)$/, cacheFirst);
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<script>
|
||||||
|
window.__rocketServiceWorkerUrl = '/{{ rocketConfig.serviceWorkerName }}';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="module" inject-service-worker="" src="{{ '/_assets/scripts/registerServiceWorker.js' | asset | url }}"></script>
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<script>
|
|
||||||
{{ '_assets/_inline-scripts/serviceWorkerUpdate.js' | asset | toAbsPath | inlineFilePath | safe }}
|
|
||||||
</script>
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<meta property="og:site_name" content="{{ site.name }}"/>
|
<meta property="og:site_name" content="{{ site.name }}"/>
|
||||||
<meta property="og:type" content="website"/>
|
<meta property="og:type" content="website"/>
|
||||||
|
|
||||||
<meta property="og:image" content="{{ socialMediaImage }}"/>
|
<meta property="og:image" content="{{ socialMediaImage | url }}"/>
|
||||||
<meta property="og:url" content="{{ page.url }}"/>
|
<meta property="og:url" content="{{ page.url | url }}"/>
|
||||||
|
|
||||||
<meta name="twitter:card" content="summary_large_image"/>
|
<meta name="twitter:card" content="summary_large_image"/>
|
||||||
|
|||||||
69
packages/cli/preset/_includes/layout-simulator.njk
Normal file
69
packages/cli/preset/_includes/layout-simulator.njk
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<html theme="light" platform="web" lang="en">
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
height: fit-content;
|
||||||
|
}
|
||||||
|
html[edge-distance] body {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script type="module">
|
||||||
|
import { render } from 'lit-html';
|
||||||
|
|
||||||
|
async function onHashChange() {
|
||||||
|
const urlParts = new URLSearchParams(document.location.hash.substr(1));
|
||||||
|
|
||||||
|
if (urlParts.get('stylesheets')) {
|
||||||
|
for (const stylesheet of urlParts.getAll('stylesheets')) {
|
||||||
|
if (!document.querySelector(`link[rel="stylesheet"][href="${stylesheet}"]`)) {
|
||||||
|
const link = document.createElement('link');
|
||||||
|
link.rel = 'stylesheet';
|
||||||
|
link.href = stylesheet;
|
||||||
|
document.head.appendChild(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (urlParts.get('theme')) {
|
||||||
|
document.documentElement.setAttribute('theme', urlParts.get('theme'));
|
||||||
|
}
|
||||||
|
if (urlParts.get('platform')) {
|
||||||
|
document.documentElement.setAttribute('platform', urlParts.get('platform'));
|
||||||
|
}
|
||||||
|
if (urlParts.get('language')) {
|
||||||
|
document.documentElement.setAttribute('lang', urlParts.get('language'));
|
||||||
|
document.documentElement.setAttribute('data-lang', urlParts.get('language'));
|
||||||
|
}
|
||||||
|
if (urlParts.get('story-key')) {
|
||||||
|
document.documentElement.setAttribute('story-key', urlParts.get('story-key'));
|
||||||
|
}
|
||||||
|
if (urlParts.get('edge-distance') === 'true') {
|
||||||
|
document.documentElement.setAttribute('edge-distance', '');
|
||||||
|
} else {
|
||||||
|
document.documentElement.removeAttribute('edge-distance');
|
||||||
|
}
|
||||||
|
|
||||||
|
const mod = await import(urlParts.get('story-file'));
|
||||||
|
render(mod[urlParts.get('story-key')]({ shadowRoot: document }), document.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('hashchange', onHashChange, false);
|
||||||
|
onHashChange();
|
||||||
|
|
||||||
|
const observer = new ResizeObserver(() => {
|
||||||
|
const dimensions = document.body.getBoundingClientRect();
|
||||||
|
const data = {
|
||||||
|
action: 'mdjs-viewer-resize',
|
||||||
|
storyKey: document.documentElement.getAttribute('story-key'),
|
||||||
|
width: dimensions.width,
|
||||||
|
height: dimensions.height,
|
||||||
|
};
|
||||||
|
parent.postMessage(JSON.stringify(data), '*');
|
||||||
|
});
|
||||||
|
observer.observe(document.body);
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body></body>
|
||||||
|
</html>
|
||||||
@@ -3,9 +3,10 @@
|
|||||||
|
|
||||||
import { rollup } from 'rollup';
|
import { rollup } from 'rollup';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
|
import path from 'path';
|
||||||
import { copy } from '@web/rollup-plugin-copy';
|
import { copy } from '@web/rollup-plugin-copy';
|
||||||
|
|
||||||
import { createMpaConfig } from '@rocket/building-rollup';
|
import { createMpaConfig, createServiceWorkerConfig } from '@rocket/building-rollup';
|
||||||
import { addPlugin, adjustPluginOptions } from 'plugins-manager';
|
import { addPlugin, adjustPluginOptions } from 'plugins-manager';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,7 +46,7 @@ async function productionBuild(config) {
|
|||||||
dir: config.outputDir,
|
dir: config.outputDir,
|
||||||
},
|
},
|
||||||
// custom
|
// custom
|
||||||
rootDir: config.outputDevDir,
|
rootDir: path.resolve(config.outputDevDir),
|
||||||
absoluteBaseUrl: config.absoluteBaseUrl,
|
absoluteBaseUrl: config.absoluteBaseUrl,
|
||||||
setupPlugins: [
|
setupPlugins: [
|
||||||
...defaultSetupPlugins,
|
...defaultSetupPlugins,
|
||||||
@@ -55,6 +56,18 @@ async function productionBuild(config) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await buildAndWrite(mpaConfig);
|
await buildAndWrite(mpaConfig);
|
||||||
|
|
||||||
|
const serviceWorkerSourcePath = path.resolve('docs/_merged_assets/service-worker.js');
|
||||||
|
if (fs.existsSync(serviceWorkerSourcePath)) {
|
||||||
|
const serviceWorkerConfig = createServiceWorkerConfig({
|
||||||
|
input: serviceWorkerSourcePath,
|
||||||
|
output: {
|
||||||
|
file: path.join(path.resolve(config.outputDir), config.serviceWorkerName),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await buildAndWrite(serviceWorkerConfig);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RocketBuild {
|
export class RocketBuild {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import computedConfigPkg from './public/computedConfig.cjs';
|
|||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import Eleventy from '@11ty/eleventy';
|
import Eleventy from '@11ty/eleventy';
|
||||||
|
import TemplateConfig from '@11ty/eleventy/src/TemplateConfig.js';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
|
|
||||||
@@ -99,11 +100,16 @@ export class RocketCli {
|
|||||||
await this.mergePresets();
|
await this.mergePresets();
|
||||||
|
|
||||||
const elev = new RocketEleventy(_inputDirCwdRelative, outputDevDir, this);
|
const elev = new RocketEleventy(_inputDirCwdRelative, outputDevDir, this);
|
||||||
elev.isVerbose = false;
|
|
||||||
// 11ty always wants a relative path to cwd - why?
|
// 11ty always wants a relative path to cwd - why?
|
||||||
const rel = path.relative(process.cwd(), path.join(__dirname));
|
const rel = path.relative(process.cwd(), path.join(__dirname));
|
||||||
const relCwdPathToConfig = path.join(rel, 'shared', '.eleventy.cjs');
|
const relCwdPathToConfig = path.join(rel, 'shared', '.eleventy.cjs');
|
||||||
|
|
||||||
|
const config = new TemplateConfig(null, relCwdPathToConfig);
|
||||||
|
elev.config = config.getConfig();
|
||||||
|
elev.resetConfig();
|
||||||
elev.setConfigPathOverride(relCwdPathToConfig);
|
elev.setConfigPathOverride(relCwdPathToConfig);
|
||||||
|
|
||||||
|
elev.isVerbose = false;
|
||||||
await elev.init();
|
await elev.init();
|
||||||
|
|
||||||
this.eleventy = elev;
|
this.eleventy = elev;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const slash = require('slash');
|
||||||
const { readdirSync } = require('fs');
|
const { readdirSync } = require('fs');
|
||||||
|
|
||||||
function getDirectories(source) {
|
function getDirectories(source) {
|
||||||
@@ -23,7 +24,7 @@ const rocketCollections = {
|
|||||||
let docs = [
|
let docs = [
|
||||||
...collection.getFilteredByGlob(`${_inputDirCwdRelative}/${section}/**/*.md`),
|
...collection.getFilteredByGlob(`${_inputDirCwdRelative}/${section}/**/*.md`),
|
||||||
];
|
];
|
||||||
docs = docs.filter(page => page.inputPath !== `./${indexSection}`);
|
docs = docs.filter(page => page.inputPath !== `./${slash(indexSection)}`);
|
||||||
|
|
||||||
return docs;
|
return docs;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export async function normalizeConfig(inConfig) {
|
|||||||
inputDir: 'docs',
|
inputDir: 'docs',
|
||||||
outputDir: '_site',
|
outputDir: '_site',
|
||||||
outputDevDir: '_site-dev',
|
outputDevDir: '_site-dev',
|
||||||
|
serviceWorkerName: 'service-worker.js',
|
||||||
build: {},
|
build: {},
|
||||||
devServer: {},
|
devServer: {},
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ export function setFixtureDir(importMetaUrl) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {object} readOutputOptions
|
* @typedef {object} readOutputOptions
|
||||||
* @property {boolean} stripServiceWorker
|
|
||||||
* @property {boolean} stripToBody
|
* @property {boolean} stripToBody
|
||||||
* @property {boolean} stripStartEndWhitespace
|
* @property {boolean} stripStartEndWhitespace
|
||||||
* @property {boolean} stripScripts
|
* @property {boolean} stripScripts
|
||||||
@@ -47,7 +46,6 @@ export async function readOutput(
|
|||||||
cli,
|
cli,
|
||||||
fileName,
|
fileName,
|
||||||
{
|
{
|
||||||
stripServiceWorker = false,
|
|
||||||
stripToBody = false,
|
stripToBody = false,
|
||||||
stripStartEndWhitespace = true,
|
stripStartEndWhitespace = true,
|
||||||
stripScripts = false,
|
stripScripts = false,
|
||||||
@@ -67,11 +65,6 @@ export async function readOutput(
|
|||||||
const bodyCloseTagStart = text.indexOf('</body>');
|
const bodyCloseTagStart = text.indexOf('</body>');
|
||||||
text = text.substring(bodyOpenTagEnd, bodyCloseTagStart);
|
text = text.substring(bodyOpenTagEnd, bodyCloseTagStart);
|
||||||
}
|
}
|
||||||
if (stripServiceWorker) {
|
|
||||||
const scriptOpenTagEnd = text.indexOf('<script inject-service-worker');
|
|
||||||
const scriptCloseTagStart = text.indexOf('</script>', scriptOpenTagEnd) + 9;
|
|
||||||
text = text.substring(0, scriptOpenTagEnd) + text.substring(scriptCloseTagStart);
|
|
||||||
}
|
|
||||||
if (stripScripts) {
|
if (stripScripts) {
|
||||||
const scriptOpenTagEnd = text.indexOf('<script>');
|
const scriptOpenTagEnd = text.indexOf('<script>');
|
||||||
const scriptCloseTagStart = text.indexOf('</script>', scriptOpenTagEnd) + 9;
|
const scriptCloseTagStart = text.indexOf('</script>', scriptOpenTagEnd) + 9;
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ describe('RocketCli computedConfig', () => {
|
|||||||
|
|
||||||
const indexHtml = await readBuildOutput(cli, 'index.html', {
|
const indexHtml = await readBuildOutput(cli, 'index.html', {
|
||||||
stripToBody: true,
|
stripToBody: true,
|
||||||
stripServiceWorker: true,
|
|
||||||
});
|
});
|
||||||
expect(indexHtml).to.equal('/_merged_assets/11ty-img/5893749-1200.png');
|
expect(indexHtml).to.equal('/_merged_assets/11ty-img/5893749-1200.png');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -35,8 +35,7 @@ describe('RocketCli e2e', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('eleventy in config', () => {
|
describe('eleventy in config', () => {
|
||||||
// TODO: find out while this has a side effect and breaks other tests
|
it('can modify eleventy via an elventy function in the config', async () => {
|
||||||
it.skip('can modify eleventy via an elventy function in the config', async () => {
|
|
||||||
cli = await executeStart('e2e-fixtures/content/eleventy.rocket.config.js');
|
cli = await executeStart('e2e-fixtures/content/eleventy.rocket.config.js');
|
||||||
const indexHtml = await readStartOutput(cli, 'index.html');
|
const indexHtml = await readStartOutput(cli, 'index.html');
|
||||||
expect(indexHtml).to.equal(
|
expect(indexHtml).to.equal(
|
||||||
@@ -76,9 +75,6 @@ describe('RocketCli e2e', () => {
|
|||||||
cli = await executeBuild('e2e-fixtures/rollup-plugin/devbuild-build.rocket.config.js');
|
cli = await executeBuild('e2e-fixtures/rollup-plugin/devbuild-build.rocket.config.js');
|
||||||
const inlineModule = await readBuildOutput(cli, 'e97af63d.js');
|
const inlineModule = await readBuildOutput(cli, 'e97af63d.js');
|
||||||
expect(inlineModule).to.equal('var a={test:"data"};console.log(a);');
|
expect(inlineModule).to.equal('var a={test:"data"};console.log(a);');
|
||||||
|
|
||||||
const swCode = await readBuildOutput(cli, 'my-service-worker.js');
|
|
||||||
expect(swCode).to.not.be.undefined;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can adjust the inputDir', async () => {
|
it('can adjust the inputDir', async () => {
|
||||||
@@ -112,7 +108,6 @@ describe('RocketCli e2e', () => {
|
|||||||
cli = await executeBuild('e2e-fixtures/content/pathPrefix.rocket.config.js');
|
cli = await executeBuild('e2e-fixtures/content/pathPrefix.rocket.config.js');
|
||||||
|
|
||||||
const linkHtml = await readBuildOutput(cli, 'link/index.html', {
|
const linkHtml = await readBuildOutput(cli, 'link/index.html', {
|
||||||
stripServiceWorker: true,
|
|
||||||
stripToBody: true,
|
stripToBody: true,
|
||||||
});
|
});
|
||||||
expect(linkHtml).to.equal(
|
expect(linkHtml).to.equal(
|
||||||
@@ -120,9 +115,7 @@ describe('RocketCli e2e', () => {
|
|||||||
'\n',
|
'\n',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
const assetHtml = await readBuildOutput(cli, 'use-assets/index.html', {
|
const assetHtml = await readBuildOutput(cli, 'use-assets/index.html');
|
||||||
stripServiceWorker: true,
|
|
||||||
});
|
|
||||||
expect(assetHtml).to.equal(
|
expect(assetHtml).to.equal(
|
||||||
'<html><head><link rel="stylesheet" href="../41297ffa.css">\n\n</head><body>\n\n</body></html>',
|
'<html><head><link rel="stylesheet" href="../41297ffa.css">\n\n</head><body>\n\n</body></html>',
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -86,6 +86,12 @@ describe('RocketCli preset', () => {
|
|||||||
' </div>',
|
' </div>',
|
||||||
'',
|
'',
|
||||||
' <footer id="main-footer"></footer>',
|
' <footer id="main-footer"></footer>',
|
||||||
|
'',
|
||||||
|
' <script',
|
||||||
|
' type="module"',
|
||||||
|
' inject-service-worker=""',
|
||||||
|
' src="/_merged_assets/scripts/registerServiceWorker.js"',
|
||||||
|
' ></script>',
|
||||||
' </body>',
|
' </body>',
|
||||||
'</html>',
|
'</html>',
|
||||||
].join('\n'),
|
].join('\n'),
|
||||||
|
|||||||
@@ -34,12 +34,7 @@ describe('RocketCli use cases', () => {
|
|||||||
expect(aboutHtml).to.equal(
|
expect(aboutHtml).to.equal(
|
||||||
[
|
[
|
||||||
'<p><code>about.md</code></p>',
|
'<p><code>about.md</code></p>',
|
||||||
'<script type="module">',
|
'<script type="module" src="/about/__mdjs-stories.js" mdjs-setup></script>',
|
||||||
' import { myData } from "../sub/assets/myData.js";',
|
|
||||||
' import("../sub/assets/myData.js");',
|
|
||||||
' const name = "myData";',
|
|
||||||
' import(`../sub/assets/${name}.js`);',
|
|
||||||
'</script>',
|
|
||||||
].join('\n'),
|
].join('\n'),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -47,12 +42,7 @@ describe('RocketCli use cases', () => {
|
|||||||
expect(subHtml).to.equal(
|
expect(subHtml).to.equal(
|
||||||
[
|
[
|
||||||
'<p><code>sub/index.md</code></p>',
|
'<p><code>sub/index.md</code></p>',
|
||||||
'<script type="module">',
|
'<script type="module" src="/sub/__mdjs-stories.js" mdjs-setup></script>',
|
||||||
' import { myData } from "./assets/myData.js";',
|
|
||||||
' import("./assets/myData.js");',
|
|
||||||
' const name = "myData";',
|
|
||||||
' import(`./assets/${name}.js`);',
|
|
||||||
'</script>',
|
|
||||||
].join('\n'),
|
].join('\n'),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -62,12 +52,7 @@ describe('RocketCli use cases', () => {
|
|||||||
expect(subDetailsHtml).to.equal(
|
expect(subDetailsHtml).to.equal(
|
||||||
[
|
[
|
||||||
'<p><code>sub/details.md</code></p>',
|
'<p><code>sub/details.md</code></p>',
|
||||||
'<script type="module">',
|
'<script type="module" src="/sub/details/__mdjs-stories.js" mdjs-setup></script>',
|
||||||
' import { myData } from "../assets/myData.js";',
|
|
||||||
' import("../assets/myData.js");',
|
|
||||||
' const name = "myData";',
|
|
||||||
' import(`../assets/${name}.js`);',
|
|
||||||
'</script>',
|
|
||||||
].join('\n'),
|
].join('\n'),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -75,12 +60,7 @@ describe('RocketCli use cases', () => {
|
|||||||
expect(indexHtml).to.equal(
|
expect(indexHtml).to.equal(
|
||||||
[
|
[
|
||||||
'<p><code>index.md</code></p>',
|
'<p><code>index.md</code></p>',
|
||||||
'<script type="module">',
|
'<script type="module" src="/__mdjs-stories.js" mdjs-setup></script>',
|
||||||
' import { myData } from "./sub/assets/myData.js";',
|
|
||||||
' import("./sub/assets/myData.js");',
|
|
||||||
' const name = "myData";',
|
|
||||||
' import(`./sub/assets/${name}.js`);',
|
|
||||||
'</script>',
|
|
||||||
].join('\n'),
|
].join('\n'),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,19 +1,11 @@
|
|||||||
// @ts-no-check
|
// @ts-no-check
|
||||||
import path from 'path';
|
|
||||||
import { fileURLToPath } from 'url';
|
|
||||||
|
|
||||||
import json from '@rollup/plugin-json';
|
import json from '@rollup/plugin-json';
|
||||||
import { addPlugin, adjustPluginOptions } from 'plugins-manager';
|
import { addPlugin, adjustPluginOptions } from 'plugins-manager';
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
||||||
const outputDir = path.join(__dirname, '__output');
|
|
||||||
|
|
||||||
/** @type {Partial<import("../../../types/main").RocketCliOptions>} */
|
/** @type {Partial<import("../../../types/main").RocketCliOptions>} */
|
||||||
const config = {
|
const config = {
|
||||||
setupDevAndBuildPlugins: [addPlugin({ name: 'json', plugin: json, location: 'top' })],
|
setupDevAndBuildPlugins: [addPlugin({ name: 'json', plugin: json, location: 'top' })],
|
||||||
setupBuildPlugins: [
|
setupBuildPlugins: [adjustPluginOptions('html', { absoluteBaseUrl: 'https://test-me.com' })],
|
||||||
adjustPluginOptions('workbox', { swDest: path.join(outputDir, 'my-service-worker.js') }),
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|||||||
@@ -31,12 +31,7 @@ export async function expectThrowsAsync(method, { errorMatch, errorMessage } = {
|
|||||||
export async function readOutput(
|
export async function readOutput(
|
||||||
cli,
|
cli,
|
||||||
fileName,
|
fileName,
|
||||||
{
|
{ stripToBody = false, stripStartEndWhitespace = true, type = 'build' } = {},
|
||||||
stripServiceWorker = false,
|
|
||||||
stripToBody = false,
|
|
||||||
stripStartEndWhitespace = true,
|
|
||||||
type = 'build',
|
|
||||||
} = {},
|
|
||||||
) {
|
) {
|
||||||
const outputDir = type === 'build' ? cli.config.outputDir : cli.config.outputDevDir;
|
const outputDir = type === 'build' ? cli.config.outputDir : cli.config.outputDevDir;
|
||||||
let text = await fs.promises.readFile(path.join(outputDir, fileName));
|
let text = await fs.promises.readFile(path.join(outputDir, fileName));
|
||||||
@@ -46,11 +41,6 @@ export async function readOutput(
|
|||||||
const bodyCloseTagStart = text.indexOf('</body>');
|
const bodyCloseTagStart = text.indexOf('</body>');
|
||||||
text = text.substring(bodyOpenTagEnd, bodyCloseTagStart);
|
text = text.substring(bodyOpenTagEnd, bodyCloseTagStart);
|
||||||
}
|
}
|
||||||
if (stripServiceWorker) {
|
|
||||||
const scriptOpenTagEnd = text.indexOf('<script inject-service-worker');
|
|
||||||
const scriptCloseTagStart = text.indexOf('</script>', scriptOpenTagEnd) + 9;
|
|
||||||
text = text.substring(0, scriptOpenTagEnd) + text.substring(scriptCloseTagStart);
|
|
||||||
}
|
|
||||||
if (stripStartEndWhitespace) {
|
if (stripStartEndWhitespace) {
|
||||||
text = text.trim();
|
text = text.trim();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ describe('normalizeConfig', () => {
|
|||||||
setupEleventyComputedConfig: [],
|
setupEleventyComputedConfig: [],
|
||||||
setupCliPlugins: [],
|
setupCliPlugins: [],
|
||||||
presets: [],
|
presets: [],
|
||||||
|
serviceWorkerName: 'service-worker.js',
|
||||||
plugins: [
|
plugins: [
|
||||||
{ commands: ['start'] },
|
{ commands: ['start'] },
|
||||||
{ commands: ['build'] },
|
{ commands: ['build'] },
|
||||||
@@ -76,6 +77,7 @@ describe('normalizeConfig', () => {
|
|||||||
setupCliPlugins: [],
|
setupCliPlugins: [],
|
||||||
setupEleventyComputedConfig: [],
|
setupEleventyComputedConfig: [],
|
||||||
presets: [],
|
presets: [],
|
||||||
|
serviceWorkerName: 'service-worker.js',
|
||||||
plugins: [
|
plugins: [
|
||||||
{ commands: ['start'] },
|
{ commands: ['start'] },
|
||||||
{ commands: ['build'] },
|
{ commands: ['build'] },
|
||||||
@@ -108,6 +110,7 @@ describe('normalizeConfig', () => {
|
|||||||
setupCliPlugins: [],
|
setupCliPlugins: [],
|
||||||
setupEleventyComputedConfig: [],
|
setupEleventyComputedConfig: [],
|
||||||
presets: [],
|
presets: [],
|
||||||
|
serviceWorkerName: 'service-worker.js',
|
||||||
plugins: [
|
plugins: [
|
||||||
{ commands: ['start'] },
|
{ commands: ['start'] },
|
||||||
{ commands: ['build'] },
|
{ commands: ['build'] },
|
||||||
@@ -143,6 +146,7 @@ describe('normalizeConfig', () => {
|
|||||||
setupCliPlugins: [],
|
setupCliPlugins: [],
|
||||||
setupEleventyComputedConfig: [],
|
setupEleventyComputedConfig: [],
|
||||||
presets: [],
|
presets: [],
|
||||||
|
serviceWorkerName: 'service-worker.js',
|
||||||
plugins: [
|
plugins: [
|
||||||
{ commands: ['start'] },
|
{ commands: ['start'] },
|
||||||
{ commands: ['build'] },
|
{ commands: ['build'] },
|
||||||
|
|||||||
1
packages/cli/types/main.d.ts
vendored
1
packages/cli/types/main.d.ts
vendored
@@ -20,6 +20,7 @@ interface RocketStartConfig {
|
|||||||
export interface RocketCliOptions {
|
export interface RocketCliOptions {
|
||||||
presets: Array<RocketPreset>;
|
presets: Array<RocketPreset>;
|
||||||
pathPrefix?: string;
|
pathPrefix?: string;
|
||||||
|
serviceWorkerName?: string;
|
||||||
inputDir: string;
|
inputDir: string;
|
||||||
outputDir: string;
|
outputDir: string;
|
||||||
emptyOutputDir?: boolean;
|
emptyOutputDir?: boolean;
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# @rocket/core
|
# @rocket/core
|
||||||
|
|
||||||
|
## 0.1.2
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- be0d0b3: fix: add missing main entry to the packages
|
||||||
|
|
||||||
## 0.1.1
|
## 0.1.1
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@rocket/core",
|
"name": "@rocket/core",
|
||||||
"version": "0.1.1",
|
"version": "0.1.2",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
},
|
},
|
||||||
"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/",
|
"homepage": "https://rocket.modern-web.dev/",
|
||||||
|
"main": "./dist/title.cjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
"./title": {
|
"./title": {
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# @rocket/drawer
|
# @rocket/drawer
|
||||||
|
|
||||||
|
## 0.1.3
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 0b64116: Update @lion dependencies
|
||||||
|
|
||||||
## 0.1.2
|
## 0.1.2
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@rocket/drawer",
|
"name": "@rocket/drawer",
|
||||||
"version": "0.1.2",
|
"version": "0.1.3",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
"testing"
|
"testing"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lion/overlays": "^0.23.2",
|
"@lion/overlays": "^0.26.1",
|
||||||
"lit-element": "^2.4.0"
|
"lit-element": "^2.4.0"
|
||||||
},
|
},
|
||||||
"types": "dist-types/index.d.ts"
|
"types": "dist-types/index.d.ts"
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export class RocketDrawer extends OverlayMixin(LitElement) {
|
|||||||
return {
|
return {
|
||||||
useOverlay: { type: Boolean, reflect: true },
|
useOverlay: { type: Boolean, reflect: true },
|
||||||
useOverlayMediaQuery: { type: String },
|
useOverlayMediaQuery: { type: String },
|
||||||
|
mediaMatcher: { type: Object },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,6 +90,20 @@ export class RocketDrawer extends OverlayMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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() {
|
_setupOpenCloseListeners() {
|
||||||
@@ -118,11 +133,15 @@ export class RocketDrawer extends OverlayMixin(LitElement) {
|
|||||||
|
|
||||||
this.__toggle = this.__toggle.bind(this);
|
this.__toggle = this.__toggle.bind(this);
|
||||||
|
|
||||||
|
this.onMatchMedia = this.onMatchMedia.bind(this);
|
||||||
this.onGestureStart = this.onGestureStart.bind(this);
|
this.onGestureStart = this.onGestureStart.bind(this);
|
||||||
this.onGestureMove = this.onGestureMove.bind(this);
|
this.onGestureMove = this.onGestureMove.bind(this);
|
||||||
this.onGestureEnd = this.onGestureEnd.bind(this);
|
this.onGestureEnd = this.onGestureEnd.bind(this);
|
||||||
this.updateFromTouch = this.updateFromTouch.bind(this);
|
this.updateFromTouch = this.updateFromTouch.bind(this);
|
||||||
|
|
||||||
|
this.mediaMatcher = window.matchMedia(this.useOverlayMediaQuery);
|
||||||
|
this.mediaMatcher.addEventListener('change', this.onMatchMedia);
|
||||||
|
|
||||||
this._startX = 0;
|
this._startX = 0;
|
||||||
this._currentX = 0;
|
this._currentX = 0;
|
||||||
this._velocity = 0;
|
this._velocity = 0;
|
||||||
@@ -133,10 +152,7 @@ export class RocketDrawer extends OverlayMixin(LitElement) {
|
|||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
this.useOverlay = !!window.matchMedia(this.useOverlayMediaQuery).matches;
|
this.useOverlay = !!this.mediaMatcher.matches;
|
||||||
window.matchMedia(this.useOverlayMediaQuery).addListener(query => {
|
|
||||||
this.useOverlay = !!query.matches;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|||||||
@@ -1,5 +1,26 @@
|
|||||||
# @rocket/eleventy-plugin-mdjs-unified
|
# @rocket/eleventy-plugin-mdjs-unified
|
||||||
|
|
||||||
|
## 0.4.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 2b7f1ee: Add support for pathprefix
|
||||||
|
- Updated dependencies [2b7f1ee]
|
||||||
|
- @mdjs/core@0.7.1
|
||||||
|
|
||||||
|
## 0.4.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- 2267e72: Write the mdjs JavaScript code to a file and load it from there instead of an inline script
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [a8e66d8]
|
||||||
|
- Updated dependencies [fe6a929]
|
||||||
|
- Updated dependencies [a8e66d8]
|
||||||
|
- @mdjs/core@0.7.0
|
||||||
|
|
||||||
## 0.3.1
|
## 0.3.1
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@rocket/eleventy-plugin-mdjs-unified",
|
"name": "@rocket/eleventy-plugin-mdjs-unified",
|
||||||
"version": "0.3.1",
|
"version": "0.4.1",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"mdjs"
|
"mdjs"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdjs/core": "^0.6.2",
|
"@mdjs/core": "^0.7.1",
|
||||||
"es-module-lexer": "^0.3.26",
|
"es-module-lexer": "^0.3.26",
|
||||||
"unist-util-visit": "^2.0.3"
|
"unist-util-visit": "^2.0.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||||
|
const path = require('path');
|
||||||
|
const slash = require('slash');
|
||||||
|
const fs = require('fs');
|
||||||
const { mdjsProcess } = require('@mdjs/core');
|
const { mdjsProcess } = require('@mdjs/core');
|
||||||
const visit = require('unist-util-visit');
|
const visit = require('unist-util-visit');
|
||||||
const { init, parse } = require('es-module-lexer');
|
const { init, parse } = require('es-module-lexer');
|
||||||
@@ -7,7 +10,7 @@ const { init, parse } = require('es-module-lexer');
|
|||||||
const { parseTitle } = require('@rocket/core/title');
|
const { parseTitle } = require('@rocket/core/title');
|
||||||
|
|
||||||
/** @typedef {import('@mdjs/core').MdjsProcessPlugin} MdjsProcessPlugin */
|
/** @typedef {import('@mdjs/core').MdjsProcessPlugin} MdjsProcessPlugin */
|
||||||
/** @typedef {import('../types/code').EleventPluginMdjsUnified} EleventPluginMdjsUnified */
|
/** @typedef {import('../types/code').EleventyPluginMdjsUnified} EleventyPluginMdjsUnified */
|
||||||
/** @typedef {import('../types/code').NodeChildren} NodeChildren */
|
/** @typedef {import('../types/code').NodeChildren} NodeChildren */
|
||||||
/** @typedef {import('../types/code').NodeElement} NodeElement */
|
/** @typedef {import('../types/code').NodeElement} NodeElement */
|
||||||
/** @typedef {import('unist').Node} Node */
|
/** @typedef {import('unist').Node} Node */
|
||||||
@@ -93,7 +96,7 @@ async function processImports(source, inputPath) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {EleventPluginMdjsUnified} pluginOptions
|
* @param {EleventyPluginMdjsUnified} pluginOptions
|
||||||
*/
|
*/
|
||||||
function eleventyUnified(pluginOptions) {
|
function eleventyUnified(pluginOptions) {
|
||||||
/**
|
/**
|
||||||
@@ -119,8 +122,12 @@ function eleventyUnified(pluginOptions) {
|
|||||||
return plugins.map(plugin => {
|
return plugins.map(plugin => {
|
||||||
if (plugin.options) {
|
if (plugin.options) {
|
||||||
plugin.options.page = eleventySettings.page;
|
plugin.options.page = eleventySettings.page;
|
||||||
|
plugin.options.rocketConfig = eleventySettings.rocketConfig;
|
||||||
} else {
|
} else {
|
||||||
plugin.options = { page: eleventySettings.page };
|
plugin.options = {
|
||||||
|
page: eleventySettings.page,
|
||||||
|
rocketConfig: eleventySettings.rocketConfig,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return plugin;
|
return plugin;
|
||||||
});
|
});
|
||||||
@@ -139,10 +146,23 @@ function eleventyUnified(pluginOptions) {
|
|||||||
|
|
||||||
let code = result.html;
|
let code = result.html;
|
||||||
if (result.jsCode) {
|
if (result.jsCode) {
|
||||||
|
const newFolder = path.dirname(eleventySettings.page.outputPath);
|
||||||
|
const newName = path.join(newFolder, '__mdjs-stories.js');
|
||||||
|
await fs.promises.mkdir(newFolder, { recursive: true });
|
||||||
|
await fs.promises.writeFile(newName, result.jsCode, 'utf8');
|
||||||
|
|
||||||
|
let scriptUrl = eleventySettings.page.url;
|
||||||
|
if (
|
||||||
|
eleventySettings.rocketConfig &&
|
||||||
|
eleventySettings.rocketConfig.command === 'build' &&
|
||||||
|
eleventySettings.rocketConfig.pathPrefix
|
||||||
|
) {
|
||||||
|
scriptUrl = slash(
|
||||||
|
path.join(eleventySettings.rocketConfig.pathPrefix, eleventySettings.page.url),
|
||||||
|
);
|
||||||
|
}
|
||||||
code += `
|
code += `
|
||||||
<script type="module">
|
<script type="module" src="${scriptUrl}__mdjs-stories.js" mdjs-setup></script>
|
||||||
${result.jsCode}
|
|
||||||
</script>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
return code;
|
return code;
|
||||||
@@ -157,15 +177,15 @@ function eleventyUnified(pluginOptions) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {*} eleventyConfig
|
* @param {*} eleventyConfig
|
||||||
* @param {EleventPluginMdjsUnified} [pluginOptions]
|
* @param {EleventyPluginMdjsUnified} [pluginOptions]
|
||||||
*/
|
*/
|
||||||
function configFunction(eleventyConfig, pluginOptions = {}) {
|
function configFunction(eleventyConfig, pluginOptions = {}) {
|
||||||
eleventyConfig.setLibrary('md', eleventyUnified(pluginOptions));
|
eleventyConfig.setLibrary('md', eleventyUnified(pluginOptions));
|
||||||
}
|
}
|
||||||
|
|
||||||
const eleventPluginMdjsUnified = {
|
const EleventyPluginMdjsUnified = {
|
||||||
initArguments: {},
|
initArguments: {},
|
||||||
configFunction,
|
configFunction,
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = eleventPluginMdjsUnified;
|
module.exports = EleventyPluginMdjsUnified;
|
||||||
|
|||||||
@@ -53,13 +53,12 @@ describe('eleventy-plugin-mdjs-unified', () => {
|
|||||||
|
|
||||||
it('renders markdown with javascript', async () => {
|
it('renders markdown with javascript', async () => {
|
||||||
const files = await renderEleventy('./test-node/fixtures/mdjs');
|
const files = await renderEleventy('./test-node/fixtures/mdjs');
|
||||||
expect(files).to.deep.equal([
|
|
||||||
{
|
expect(files.length).to.equal(1);
|
||||||
html:
|
expect(files[0].name).to.equal('first/index.html');
|
||||||
'<h1 id="first"><a aria-hidden="true" tabindex="-1" href="#first"><span class="icon icon-link"></span></a>First</h1>\n<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> foo <span class="token operator">=</span> <span class="token string">\'bar\'</span><span class="token punctuation">;</span>\n<span class="token keyword module">import</span> <span class="token punctuation">{</span> html <span class="token punctuation">}</span> <span class="token keyword module">from</span> <span class="token string">\'lit-html\'</span><span class="token punctuation">;</span>\n</code></pre>\n<mdjs-story mdjs-story-name="inline"></mdjs-story>\n<mdjs-preview mdjs-story-name="withBorder"></mdjs-preview>\n <script type="module">\n \nexport const inline = () => html` <p>main</p> `;\nexport const withBorder = () => html` <p>main</p> `;\nconst rootNode = document;\nconst stories = [{ key: \'inline\', story: inline, code: `<pre class="language-js"><code class="language-js"><span class="token keyword module">export</span> <span class="token keyword">const</span> <span class="token function-variable function">inline</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow 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"><</span>p</span><span class="token punctuation">></span></span>main<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> </span><span class="token template-punctuation string">\\`</span></span><span class="token punctuation">;</span>\n</code></pre>` }, { key: \'withBorder\', story: withBorder, code: `<pre class="language-js"><code class="language-js"><span class="token keyword module">export</span> <span class="token keyword">const</span> <span class="token function-variable function">withBorder</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow 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"><</span>p</span><span class="token punctuation">></span></span>main<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> </span><span class="token template-punctuation string">\\`</span></span><span class="token punctuation">;</span>\n</code></pre>` }];\nfor (const story of stories) {\n const storyEl = rootNode.querySelector(`[mdjs-story-name="${story.key}"]`);\n storyEl.codeHasHtml = true;\n storyEl.story = story.story;\n storyEl.code = story.code;\n};\nif (!customElements.get(\'mdjs-preview\')) { import(\'@mdjs/mdjs-preview/mdjs-preview.js\'); }\nif (!customElements.get(\'mdjs-story\')) { import(\'@mdjs/mdjs-story/mdjs-story.js\'); }\n </script>\n ',
|
|
||||||
name: 'first/index.html',
|
expect(files[0].html).to.include('<script type="module"');
|
||||||
},
|
expect(files[0].html).to.include('mdjs-setup>');
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rewrites relative import pathes', async () => {
|
it('rewrites relative import pathes', async () => {
|
||||||
@@ -67,7 +66,7 @@ describe('eleventy-plugin-mdjs-unified', () => {
|
|||||||
expect(files).to.deep.equal([
|
expect(files).to.deep.equal([
|
||||||
{
|
{
|
||||||
html:
|
html:
|
||||||
"<p>first</p>\n <script type=\"module\">\n import '../import-me.js';\nimport('../import-me-too.js');\n </script>\n ",
|
'<p>first</p>\n <script type="module" src="/first/__mdjs-stories.js" mdjs-setup></script>\n ',
|
||||||
name: 'first/index.html',
|
name: 'first/index.html',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
@@ -78,7 +77,7 @@ describe('eleventy-plugin-mdjs-unified', () => {
|
|||||||
expect(files).to.deep.equal([
|
expect(files).to.deep.equal([
|
||||||
{
|
{
|
||||||
html:
|
html:
|
||||||
"<p>first</p>\n <script type=\"module\">\n import '../../import-me.js';\nimport('../../import-me-too.js');\n </script>\n ",
|
'<p>first</p>\n <script type="module" src="/subpage/first/__mdjs-stories.js" mdjs-setup></script>\n ',
|
||||||
name: 'subpage/first/index.html',
|
name: 'subpage/first/index.html',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
@@ -89,7 +88,7 @@ describe('eleventy-plugin-mdjs-unified', () => {
|
|||||||
expect(files).to.deep.equal([
|
expect(files).to.deep.equal([
|
||||||
{
|
{
|
||||||
html:
|
html:
|
||||||
"<p>index</p>\n <script type=\"module\">\n import './import-me.js';\nimport('./import-me-too.js');\n </script>\n ",
|
'<p>index</p>\n <script type="module" src="/__mdjs-stories.js" mdjs-setup></script>\n ',
|
||||||
name: 'index.html',
|
name: 'index.html',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Node } from 'unist';
|
|||||||
|
|
||||||
export const setupUnifiedPluginsFn: (plugins: MdjsProcessPlugin[]) => MdjsProcessPlugin[];
|
export const setupUnifiedPluginsFn: (plugins: MdjsProcessPlugin[]) => MdjsProcessPlugin[];
|
||||||
|
|
||||||
export interface EleventPluginMdjsUnified {
|
export interface EleventyPluginMdjsUnified {
|
||||||
setupUnifiedPlugins?: setupUnifiedPluginsFn[];
|
setupUnifiedPlugins?: setupUnifiedPluginsFn[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
const RocketNav = require('./eleventy-rocket-nav');
|
const RocketNav = require('./eleventy-rocket-nav');
|
||||||
|
const { addPageAnchors } = require('./src/addPageAnchors.js');
|
||||||
|
|
||||||
// export the configuration function for plugin
|
// export the configuration function for plugin
|
||||||
module.exports = function (eleventyConfig) {
|
module.exports = function (eleventyConfig) {
|
||||||
@@ -8,6 +9,10 @@ module.exports = function (eleventyConfig) {
|
|||||||
eleventyConfig.addNunjucksFilter('rocketNavToHtml', function (pages, options) {
|
eleventyConfig.addNunjucksFilter('rocketNavToHtml', function (pages, options) {
|
||||||
return RocketNav.toHtml.call(eleventyConfig, pages, options);
|
return RocketNav.toHtml.call(eleventyConfig, pages, options);
|
||||||
});
|
});
|
||||||
|
eleventyConfig.addTransform('rocket-nav-add-page-anchors', async function (content) {
|
||||||
|
const newContent = await addPageAnchors(content);
|
||||||
|
return newContent;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.navigation = {
|
module.exports.navigation = {
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
# @rocket/eleventy-rocket-nav
|
# @rocket/eleventy-rocket-nav
|
||||||
|
|
||||||
|
## 0.3.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- ef8ebb0: To support dynamically created content to be part of the anchor navigation of the page we now analyze the final html output instead of `entry.templateContent`.
|
||||||
|
|
||||||
|
BREAKING CHANGE:
|
||||||
|
|
||||||
|
- only add anchors for the currently active pages (before it added anchor for every page)
|
||||||
|
|
||||||
## 0.2.1
|
## 0.2.1
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -94,21 +94,8 @@ function findNavigationEntries(nodes = [], key = '') {
|
|||||||
entry.title = entry.key;
|
entry.title = entry.key;
|
||||||
}
|
}
|
||||||
if (entry.key) {
|
if (entry.key) {
|
||||||
if (!headingsCache.has(entry.templateContent)) {
|
|
||||||
headingsCache.set(entry.templateContent, getHeadingsOfHtml(entry.templateContent));
|
|
||||||
}
|
|
||||||
const headings = /** @type {Heading[]} */ (headingsCache.get(entry.templateContent));
|
|
||||||
const anchors = headings.map(heading => ({
|
|
||||||
key: heading.text + Math.random(),
|
|
||||||
parent: entry.key,
|
|
||||||
url: `${entry.url}#${heading.id}`,
|
|
||||||
pluginType: 'eleventy-navigation',
|
|
||||||
parentKey: entry.key,
|
|
||||||
title: heading.text,
|
|
||||||
anchor: true,
|
|
||||||
}));
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
entry.children = [...anchors, ...findNavigationEntries(nodes, entry.key)];
|
entry.children = findNavigationEntries(nodes, entry.key);
|
||||||
}
|
}
|
||||||
return entry;
|
return entry;
|
||||||
});
|
});
|
||||||
@@ -227,43 +214,36 @@ function navigationToHtml(pages, _options = {}) {
|
|||||||
}>${pages
|
}>${pages
|
||||||
.map(entry => {
|
.map(entry => {
|
||||||
const liClass = [];
|
const liClass = [];
|
||||||
const aClass = [];
|
|
||||||
if (options.listItemClass) {
|
if (options.listItemClass) {
|
||||||
liClass.push(options.listItemClass);
|
liClass.push(options.listItemClass);
|
||||||
}
|
}
|
||||||
if (options.anchorClass) {
|
if (options.activeKey === entry.key && options.activeListItemClass) {
|
||||||
aClass.push(options.anchorClass);
|
liClass.push(options.activeListItemClass);
|
||||||
}
|
|
||||||
if (options.activeKey === entry.key) {
|
|
||||||
if (options.activeListItemClass) {
|
|
||||||
liClass.push(options.activeListItemClass);
|
|
||||||
}
|
|
||||||
if (options.activeAnchorClass) {
|
|
||||||
aClass.push(options.activeAnchorClass);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (options.activeTreeListClass && activePages && activePages.includes(entry.key)) {
|
if (options.activeTreeListClass && activePages && activePages.includes(entry.key)) {
|
||||||
liClass.push(options.activeTreeListClass);
|
liClass.push(options.activeTreeListClass);
|
||||||
}
|
}
|
||||||
if (options.activeAnchorListClass && activePages && activePages.includes(entry.key)) {
|
|
||||||
aClass.push(options.activeAnchorListClass);
|
|
||||||
}
|
|
||||||
if (options.listItemHasChildrenClass && entry.children && entry.children.length) {
|
if (options.listItemHasChildrenClass && entry.children && entry.children.length) {
|
||||||
liClass.push(options.listItemHasChildrenClass);
|
liClass.push(options.listItemHasChildrenClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.anchor) {
|
const output = [];
|
||||||
liClass.push('anchor');
|
output.push(
|
||||||
aClass.push('anchor');
|
`<${options.listItemElement}${liClass.length ? ` class="${liClass.join(' ')}"` : ''}>`,
|
||||||
|
);
|
||||||
|
output.push(`<a href="${urlFilter(entry.url)}">${entry.title}</a>`);
|
||||||
|
if (options.showExcerpt && entry.excerpt) {
|
||||||
|
output.push(`: ${entry.excerpt}`);
|
||||||
}
|
}
|
||||||
|
if (options.activeKey === entry.key && options.activeListItemClass) {
|
||||||
|
output.push('<!-- ADD PAGE ANCHORS -->');
|
||||||
|
}
|
||||||
|
if (entry.children) {
|
||||||
|
output.push(navigationToHtml(entry.children, options));
|
||||||
|
}
|
||||||
|
output.push(`</${options.listItemElement}>`);
|
||||||
|
|
||||||
return `<${options.listItemElement}${
|
return output.join('\n');
|
||||||
liClass.length ? ` class="${liClass.join(' ')}"` : ''
|
|
||||||
}><a href="${urlFilter(entry.url)}"${
|
|
||||||
aClass.length ? ` class="${aClass.join(' ')}"` : ''
|
|
||||||
}>${entry.title}</a>${options.showExcerpt && entry.excerpt ? `: ${entry.excerpt}` : ''}${
|
|
||||||
entry.children ? navigationToHtml(entry.children, options) : ''
|
|
||||||
}</${options.listItemElement}>`;
|
|
||||||
})
|
})
|
||||||
.join('\n')}</${options.listElement}>`
|
.join('\n')}</${options.listElement}>`
|
||||||
: '';
|
: '';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@rocket/eleventy-rocket-nav",
|
"name": "@rocket/eleventy-rocket-nav",
|
||||||
"version": "0.2.1",
|
"version": "0.3.0",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
},
|
},
|
||||||
"main": ".eleventy.js",
|
"main": ".eleventy.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "mocha test-node/**/*.test.js test-node/*.test.js",
|
"test": "mocha test-node/**/*.test.js test-node/*.test.js --timeout 5000",
|
||||||
"test:watch": "mocha test-node/**/*.test.js test-node/*.test.js --watch"
|
"test:watch": "mocha test-node/**/*.test.js test-node/*.test.js --watch"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
|||||||
136
packages/eleventy-rocket-nav/src/addPageAnchors.js
Normal file
136
packages/eleventy-rocket-nav/src/addPageAnchors.js
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||||
|
const fs = require('fs');
|
||||||
|
const { SaxEventType, SAXParser } = require('sax-wasm');
|
||||||
|
|
||||||
|
const saxPath = require.resolve('sax-wasm/lib/sax-wasm.wasm');
|
||||||
|
const saxWasmBuffer = fs.readFileSync(saxPath);
|
||||||
|
|
||||||
|
/** @typedef {import('../types').Heading} Heading */
|
||||||
|
|
||||||
|
/** @typedef {import('sax-wasm').Text} Text */
|
||||||
|
/** @typedef {import('sax-wasm').Tag} Tag */
|
||||||
|
/** @typedef {import('sax-wasm').Position} Position */
|
||||||
|
|
||||||
|
// Instantiate
|
||||||
|
const parser = new SAXParser(
|
||||||
|
SaxEventType.CloseTag | SaxEventType.Comment,
|
||||||
|
{ highWaterMark: 256 * 1024 }, // 256k chunks
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {object} options
|
||||||
|
* @param {string} options.content
|
||||||
|
* @param {Position} options.start
|
||||||
|
* @param {Position} options.end
|
||||||
|
* @param {string} options.insert
|
||||||
|
*/
|
||||||
|
function removeBetween({ content, start, end, insert = '' }) {
|
||||||
|
const lines = content.split('\n');
|
||||||
|
const i = start.line;
|
||||||
|
const line = lines[i];
|
||||||
|
const upToChange = line.slice(0, start.character - 1);
|
||||||
|
const afterChange = line.slice(end.character + 2);
|
||||||
|
|
||||||
|
lines[i] = `${upToChange}${insert}${afterChange}`;
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Tag} data
|
||||||
|
* @param {string} name
|
||||||
|
*/
|
||||||
|
function getAttribute(data, name) {
|
||||||
|
if (data.attributes) {
|
||||||
|
const { attributes } = data;
|
||||||
|
const foundIndex = attributes.findIndex(entry => entry.name.value === name);
|
||||||
|
if (foundIndex !== -1) {
|
||||||
|
return attributes[foundIndex].value.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Tag} data
|
||||||
|
*/
|
||||||
|
function getText(data) {
|
||||||
|
if (data.textNodes) {
|
||||||
|
return data.textNodes.map(textNode => textNode.value).join('');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} html
|
||||||
|
*/
|
||||||
|
function getHeadingsOfHtml(html) {
|
||||||
|
/** @type {Heading[]} */
|
||||||
|
const headings = [];
|
||||||
|
/** @type {Text} */
|
||||||
|
let insertPoint;
|
||||||
|
parser.eventHandler = (ev, _data) => {
|
||||||
|
if (ev === SaxEventType.Comment) {
|
||||||
|
const data = /** @type {Text} */ (/** @type {any} */ (_data));
|
||||||
|
// NOTE: we NEED to access data internal value so sax-wasm does not reuse it's value
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const tmp = data.start.line + data.end.line;
|
||||||
|
if (data.value.trim() === 'ADD PAGE ANCHORS' || data.value.trim() === '-->ADD PAGE ANCHORS') {
|
||||||
|
insertPoint = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev === SaxEventType.CloseTag) {
|
||||||
|
const data = /** @type {Tag} */ (/** @type {any} */ (_data));
|
||||||
|
if (data.name === 'h2') {
|
||||||
|
const id = getAttribute(data, 'id');
|
||||||
|
const text = getText(data);
|
||||||
|
if (id && text) {
|
||||||
|
headings.push({ text, id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
parser.write(Buffer.from(html, 'utf8'));
|
||||||
|
parser.end();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return { headings, insertPoint };
|
||||||
|
}
|
||||||
|
|
||||||
|
let isSetup = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} content
|
||||||
|
*/
|
||||||
|
async function addPageAnchors(content) {
|
||||||
|
if (!isSetup) {
|
||||||
|
await parser.prepareWasm(saxWasmBuffer);
|
||||||
|
isSetup = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { headings, insertPoint } = getHeadingsOfHtml(content);
|
||||||
|
const pageAnchorsHtml = [];
|
||||||
|
if (headings.length > 0) {
|
||||||
|
pageAnchorsHtml.push('<ul>');
|
||||||
|
for (const heading of headings) {
|
||||||
|
pageAnchorsHtml.push(' <li class="menu-item anchor">');
|
||||||
|
pageAnchorsHtml.push(` <a href="#${heading.id}" class="anchor">${heading.text}</a>`);
|
||||||
|
pageAnchorsHtml.push(' </li>');
|
||||||
|
}
|
||||||
|
pageAnchorsHtml.push('</ul>');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insertPoint) {
|
||||||
|
return removeBetween({
|
||||||
|
content,
|
||||||
|
start: insertPoint.start,
|
||||||
|
end: insertPoint.end,
|
||||||
|
insert: pageAnchorsHtml.join('\n'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
addPageAnchors,
|
||||||
|
};
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
const { expect } = require('chai');
|
||||||
|
const prettier = require('prettier');
|
||||||
|
const { addPageAnchors } = require('../src/addPageAnchors.js');
|
||||||
|
|
||||||
|
const format = code => prettier.format(code, { parser: 'html' }).trim();
|
||||||
|
|
||||||
|
describe('addPageAnchors', () => {
|
||||||
|
it('finds and adds anchors for each h2 as an unordered list', async () => {
|
||||||
|
const input = [
|
||||||
|
'<body>',
|
||||||
|
' <!-- ADD PAGE ANCHORS -->',
|
||||||
|
' <div id="content">',
|
||||||
|
' <h2 id="first">👉 First Headline</h2>',
|
||||||
|
' </div>',
|
||||||
|
'</body>',
|
||||||
|
].join('\n');
|
||||||
|
const expected = [
|
||||||
|
'<body>',
|
||||||
|
' <ul>',
|
||||||
|
' <li class="menu-item anchor">',
|
||||||
|
' <a href="#first" class="anchor">👉 First Headline</a>',
|
||||||
|
' </li>',
|
||||||
|
' </ul>',
|
||||||
|
' <div id="content">',
|
||||||
|
' <h2 id="first">👉 First Headline</h2>',
|
||||||
|
' </div>',
|
||||||
|
'</body>',
|
||||||
|
].join('\n');
|
||||||
|
const result = await addPageAnchors(input);
|
||||||
|
expect(format(result)).to.deep.equal(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
const eleventyNavigationPlugin = require('@rocket/eleventy-rocket-nav');
|
||||||
|
|
||||||
|
module.exports = function (eleventyConfig) {
|
||||||
|
eleventyConfig.addPlugin(eleventyNavigationPlugin);
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
module.exports = 'layout.njk';
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<body>
|
||||||
|
{{ collections.all | rocketNav | rocketNavToHtml({
|
||||||
|
listItemClass: "menu-item",
|
||||||
|
activeListItemClass: "current",
|
||||||
|
activeKey: eleventyNavigation.key
|
||||||
|
}) | safe }}
|
||||||
|
|
||||||
|
{{ content | safe }}
|
||||||
|
</body>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
title: Bats
|
||||||
|
eleventyNavigation:
|
||||||
|
key: Bats
|
||||||
|
parent: Mammals
|
||||||
|
order: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
🦇 can fly.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
title: Humans
|
||||||
|
eleventyNavigation:
|
||||||
|
key: Humans
|
||||||
|
parent: Mammals
|
||||||
|
order: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
<h2 id="anatomy">Anatomy</h2>
|
||||||
|
<p>Has arms.</p>
|
||||||
|
<h2 id="age">📖 Age</h2>
|
||||||
|
<p>Up to 130 years.</p>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
title: Mammals
|
||||||
|
eleventyNavigation:
|
||||||
|
key: Mammals
|
||||||
|
---
|
||||||
|
|
||||||
|
Mammals need air.
|
||||||
96
packages/eleventy-rocket-nav/test-node/integration.test.js
Normal file
96
packages/eleventy-rocket-nav/test-node/integration.test.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const { expect } = require('chai');
|
||||||
|
const Eleventy = require('@11ty/eleventy');
|
||||||
|
const TemplateConfig = require('@11ty/eleventy/src/TemplateConfig');
|
||||||
|
const prettier = require('prettier');
|
||||||
|
|
||||||
|
async function execute(fixtureDir) {
|
||||||
|
const relPath = path.relative(process.cwd(), __dirname);
|
||||||
|
const relativeInputPath = path.join(relPath, fixtureDir.split('/').join(path.sep));
|
||||||
|
const relativeOutputPath = path.join(relPath, 'fixtures', '__output');
|
||||||
|
const relativeConfigPath = path.join(relativeInputPath, '.eleventy.js');
|
||||||
|
|
||||||
|
await fs.emptyDir(relativeOutputPath);
|
||||||
|
|
||||||
|
const elev = new Eleventy(relativeInputPath, relativeOutputPath);
|
||||||
|
const config = new TemplateConfig(null, relativeConfigPath);
|
||||||
|
elev.config = config.getConfig();
|
||||||
|
elev.setConfigPathOverride(relativeConfigPath);
|
||||||
|
elev.resetConfig();
|
||||||
|
|
||||||
|
await elev.init();
|
||||||
|
await elev.write();
|
||||||
|
return {
|
||||||
|
readOutput: async readPath => {
|
||||||
|
const relativeReadPath = path.join(relativeOutputPath, readPath);
|
||||||
|
let text = await fs.promises.readFile(relativeReadPath);
|
||||||
|
text = text.toString();
|
||||||
|
text = prettier.format(text, { parser: 'html', printWidth: 100 });
|
||||||
|
return text.trim();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('eleventy-rocket-nav', () => {
|
||||||
|
it('renders a menu with anchors for h2 content', async () => {
|
||||||
|
const { readOutput } = await execute('fixtures/three-pages');
|
||||||
|
|
||||||
|
const bats = await readOutput('bats/index.html');
|
||||||
|
expect(bats).to.deep.equal(
|
||||||
|
[
|
||||||
|
'<body>',
|
||||||
|
' <ul>',
|
||||||
|
' <li class="menu-item active">',
|
||||||
|
' <a href="/mammals/">Mammals</a>',
|
||||||
|
' <ul>',
|
||||||
|
' <li class="menu-item">',
|
||||||
|
' <a href="/humans/">Humans</a>',
|
||||||
|
' </li>',
|
||||||
|
' <li class="menu-item current">',
|
||||||
|
' <a href="/bats/">Bats</a>',
|
||||||
|
' </li>',
|
||||||
|
' </ul>',
|
||||||
|
' </li>',
|
||||||
|
' </ul>',
|
||||||
|
'',
|
||||||
|
' <p>🦇 can fly.</p>',
|
||||||
|
'</body>',
|
||||||
|
].join('\n'),
|
||||||
|
);
|
||||||
|
|
||||||
|
const humans = await readOutput('humans/index.html');
|
||||||
|
expect(humans).to.deep.equal(
|
||||||
|
[
|
||||||
|
'<body>',
|
||||||
|
' <ul>',
|
||||||
|
' <li class="menu-item active">',
|
||||||
|
' <a href="/mammals/">Mammals</a>',
|
||||||
|
' <ul>',
|
||||||
|
' <li class="menu-item current">',
|
||||||
|
' <a href="/humans/">Humans</a>',
|
||||||
|
' <ul>',
|
||||||
|
' <li class="menu-item anchor">',
|
||||||
|
' <a href="#anatomy" class="anchor">Anatomy</a>',
|
||||||
|
' </li>',
|
||||||
|
' <li class="menu-item anchor">',
|
||||||
|
' <a href="#age" class="anchor">📖 Age</a>',
|
||||||
|
' </li>',
|
||||||
|
' </ul>',
|
||||||
|
' </li>',
|
||||||
|
' <li class="menu-item">',
|
||||||
|
' <a href="/bats/">Bats</a>',
|
||||||
|
' </li>',
|
||||||
|
' </ul>',
|
||||||
|
' </li>',
|
||||||
|
' </ul>',
|
||||||
|
'',
|
||||||
|
' <h2 id="anatomy">Anatomy</h2>',
|
||||||
|
' <p>Has arms.</p>',
|
||||||
|
' <h2 id="age">📖 Age</h2>',
|
||||||
|
' <p>Up to 130 years.</p>',
|
||||||
|
'</body>',
|
||||||
|
].join('\n'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,5 +1,17 @@
|
|||||||
# @rocket/launch
|
# @rocket/launch
|
||||||
|
|
||||||
|
## 0.4.2
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 2b7f1ee: Add support for pathprefix
|
||||||
|
|
||||||
|
## 0.4.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 81edf45: Reduce the amount of js files in the build by avoiding inline script tags
|
||||||
|
|
||||||
## 0.4.0
|
## 0.4.0
|
||||||
|
|
||||||
### Minor Changes
|
### Minor Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@rocket/launch",
|
"name": "@rocket/launch",
|
||||||
"version": "0.4.0",
|
"version": "0.4.2",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
"preset"
|
"preset"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rocket/drawer": "^0.1.2",
|
"@rocket/drawer": "^0.1.3",
|
||||||
"@rocket/navigation": "^0.2.0"
|
"@rocket/navigation": "^0.2.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1 @@
|
|||||||
{#
|
<script type="module" src="{{ '/_assets/scripts/init-navigation.js' | asset | url }}"></script>
|
||||||
src leads to the file not being included/executed? bug in rollup-plugin-html?
|
|
||||||
<script type="module" src="{{ '/_assets/scripts/init-navigation.js' | asset }}"></script>
|
|
||||||
#}
|
|
||||||
<script type="module">
|
|
||||||
import '@rocket/navigation/rocket-navigation.js';
|
|
||||||
import '@rocket/drawer/rocket-drawer.js';
|
|
||||||
const drawer = document.querySelector('#sidebar');
|
|
||||||
|
|
||||||
// Toggle button
|
|
||||||
const triggers = document.querySelectorAll('[data-action="trigger-mobile-menu"]');
|
|
||||||
for (const trigger of [...triggers]) {
|
|
||||||
trigger.addEventListener('click', function () {
|
|
||||||
drawer.opened = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<meta property="og:site_name" content="{{ site.name }}"/>
|
<meta property="og:site_name" content="{{ site.name }}"/>
|
||||||
<meta property="og:type" content="website"/>
|
<meta property="og:type" content="website"/>
|
||||||
|
|
||||||
<meta property="og:image" content="{{ socialMediaImage }}"/>
|
<meta property="og:image" content="{{ socialMediaImage | url }}"/>
|
||||||
<meta property="og:url" content="{{ page.url }}"/>
|
<meta property="og:url" content="{{ page.url | url }}"/>
|
||||||
|
|
||||||
<meta name="twitter:card" content="summary_large_image"/>
|
<meta name="twitter:card" content="summary_large_image"/>
|
||||||
|
|||||||
@@ -208,19 +208,13 @@ describe('RocketLaunch preset', () => {
|
|||||||
' </div>',
|
' </div>',
|
||||||
' </footer>',
|
' </footer>',
|
||||||
'',
|
'',
|
||||||
' <script type="module">',
|
' <script type="module" src="/_merged_assets/scripts/init-navigation.js"></script>',
|
||||||
' import "@rocket/navigation/rocket-navigation.js";',
|
|
||||||
' import "@rocket/drawer/rocket-drawer.js";',
|
|
||||||
' const drawer = document.querySelector("#sidebar");',
|
|
||||||
'',
|
'',
|
||||||
' // Toggle button',
|
' <script',
|
||||||
` const triggers = document.querySelectorAll('[data-action="trigger-mobile-menu"]');`,
|
' type="module"',
|
||||||
' for (const trigger of [...triggers]) {',
|
' inject-service-worker=""',
|
||||||
' trigger.addEventListener("click", function () {',
|
' src="/_merged_assets/scripts/registerServiceWorker.js"',
|
||||||
' drawer.opened = true;',
|
' ></script>',
|
||||||
' });',
|
|
||||||
' }',
|
|
||||||
' </script>',
|
|
||||||
' </body>',
|
' </body>',
|
||||||
'</html>',
|
'</html>',
|
||||||
].join('\n'),
|
].join('\n'),
|
||||||
@@ -443,19 +437,13 @@ describe('RocketLaunch preset', () => {
|
|||||||
' </div>',
|
' </div>',
|
||||||
' </footer>',
|
' </footer>',
|
||||||
'',
|
'',
|
||||||
' <script type="module">',
|
' <script type="module" src="/_merged_assets/scripts/init-navigation.js"></script>',
|
||||||
' import "@rocket/navigation/rocket-navigation.js";',
|
|
||||||
' import "@rocket/drawer/rocket-drawer.js";',
|
|
||||||
' const drawer = document.querySelector("#sidebar");',
|
|
||||||
'',
|
'',
|
||||||
' // Toggle button',
|
' <script',
|
||||||
` const triggers = document.querySelectorAll('[data-action="trigger-mobile-menu"]');`,
|
' type="module"',
|
||||||
' for (const trigger of [...triggers]) {',
|
' inject-service-worker=""',
|
||||||
' trigger.addEventListener("click", function () {',
|
' src="/_merged_assets/scripts/registerServiceWorker.js"',
|
||||||
' drawer.opened = true;',
|
' ></script>',
|
||||||
' });',
|
|
||||||
' }',
|
|
||||||
' </script>',
|
|
||||||
' </body>',
|
' </body>',
|
||||||
'</html>',
|
'</html>',
|
||||||
].join('\n'),
|
].join('\n'),
|
||||||
|
|||||||
@@ -1,5 +1,26 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## 0.7.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 2b7f1ee: Add support for pathprefix
|
||||||
|
|
||||||
|
## 0.7.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- a8e66d8: Extract building of the JavaScript setup code into a unified plugin called mdjsSetupCode
|
||||||
|
- fe6a929: For the story preview keep the original code block around to get code highlighting from the main document. This enables styling and reduces the code complexity.
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- a8e66d8: You can provide a highlightCode function to the mdjsSetupCode unified plugin
|
||||||
|
- Updated dependencies [edb1abf]
|
||||||
|
- Updated dependencies [604a80e]
|
||||||
|
- @mdjs/mdjs-preview@0.4.0
|
||||||
|
- @mdjs/mdjs-story@0.2.0
|
||||||
|
|
||||||
## 0.6.2
|
## 0.6.2
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@mdjs/core",
|
"name": "@mdjs/core",
|
||||||
"version": "0.6.2",
|
"version": "0.7.1",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
@@ -43,12 +43,12 @@
|
|||||||
"remark"
|
"remark"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdjs/mdjs-preview": "^0.3.0",
|
"@mdjs/mdjs-preview": "^0.4.2",
|
||||||
"@mdjs/mdjs-story": "^0.1.0",
|
"@mdjs/mdjs-story": "^0.2.0",
|
||||||
"@types/unist": "^2.0.3",
|
"@types/unist": "^2.0.3",
|
||||||
"es-module-lexer": "^0.3.26",
|
"es-module-lexer": "^0.3.26",
|
||||||
"github-markdown-css": "^4.0.0",
|
"github-markdown-css": "^4.0.0",
|
||||||
"plugins-manager": "^0.2.0",
|
"plugins-manager": "^0.2.1",
|
||||||
"rehype-autolink-headings": "^5.0.1",
|
"rehype-autolink-headings": "^5.0.1",
|
||||||
"rehype-prism-template": "^0.4.1",
|
"rehype-prism-template": "^0.4.1",
|
||||||
"rehype-raw": "^5.0.0",
|
"rehype-raw": "^5.0.0",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ const { executeSetupFunctions } = require('plugins-manager');
|
|||||||
|
|
||||||
const { mdjsParse } = require('./mdjsParse.js');
|
const { mdjsParse } = require('./mdjsParse.js');
|
||||||
const { mdjsStoryParse } = require('./mdjsStoryParse.js');
|
const { mdjsStoryParse } = require('./mdjsStoryParse.js');
|
||||||
|
const { mdjsSetupCode } = require('./mdjsSetupCode.js');
|
||||||
|
|
||||||
/** @type {MdjsProcessPlugin[]} */
|
/** @type {MdjsProcessPlugin[]} */
|
||||||
const defaultMetaPlugins = [
|
const defaultMetaPlugins = [
|
||||||
@@ -25,6 +26,7 @@ const defaultMetaPlugins = [
|
|||||||
{ name: 'gfm', plugin: gfm },
|
{ name: 'gfm', plugin: gfm },
|
||||||
{ name: 'mdjsParse', plugin: mdjsParse },
|
{ name: 'mdjsParse', plugin: mdjsParse },
|
||||||
{ name: 'mdjsStoryParse', plugin: mdjsStoryParse },
|
{ name: 'mdjsStoryParse', plugin: mdjsStoryParse },
|
||||||
|
{ name: 'mdjsSetupCode', plugin: mdjsSetupCode },
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
{ name: 'remark2rehype', plugin: remark2rehype, options: { allowDangerousHtml: true } },
|
{ name: 'remark2rehype', plugin: remark2rehype, options: { allowDangerousHtml: true } },
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -50,29 +52,15 @@ 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(
|
async function mdjsProcess(mdjs, { setupUnifiedPlugins = [] } = {}) {
|
||||||
mdjs,
|
|
||||||
{ rootNodeQueryCode = 'document', setupUnifiedPlugins = [] } = {},
|
|
||||||
) {
|
|
||||||
const parser = unified();
|
const parser = unified();
|
||||||
|
|
||||||
const metaPlugins = executeSetupFunctions(setupUnifiedPlugins, defaultMetaPlugins);
|
const metaPlugins = executeSetupFunctions(setupUnifiedPlugins, defaultMetaPlugins);
|
||||||
|
|
||||||
// @ts-ignore
|
/**
|
||||||
for (const pluginObj of metaPlugins) {
|
* @param {string} code
|
||||||
parser.use(pluginObj.plugin, pluginObj.options);
|
*/
|
||||||
}
|
async function highlightCode(code) {
|
||||||
|
|
||||||
/** @type {unknown} */
|
|
||||||
const parseResult = await parser.process(mdjs);
|
|
||||||
const result = /** @type {ParseResult} */ (parseResult);
|
|
||||||
|
|
||||||
const { stories, jsCode } = result.data;
|
|
||||||
let fullJsCode = jsCode;
|
|
||||||
|
|
||||||
if (stories && stories.length > 0) {
|
|
||||||
const storiesCode = stories.map(story => story.code).join('\n');
|
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const codePlugins = metaPlugins.filter(pluginObj =>
|
const codePlugins = metaPlugins.filter(pluginObj =>
|
||||||
['markdown', 'remark2rehype', 'rehypePrism', 'htmlStringify'].includes(pluginObj.name),
|
['markdown', 'remark2rehype', 'rehypePrism', 'htmlStringify'].includes(pluginObj.name),
|
||||||
@@ -82,46 +70,30 @@ async function mdjsProcess(
|
|||||||
for (const pluginObj of codePlugins) {
|
for (const pluginObj of codePlugins) {
|
||||||
codeParser.use(pluginObj.plugin, pluginObj.options);
|
codeParser.use(pluginObj.plugin, pluginObj.options);
|
||||||
}
|
}
|
||||||
|
const codeResult = await codeParser.process(code);
|
||||||
const invokeStoriesCode = [];
|
return codeResult.contents;
|
||||||
for (const story of stories) {
|
|
||||||
let code = '';
|
|
||||||
switch (story.type) {
|
|
||||||
case 'html':
|
|
||||||
code = `\`\`\`html\n${story.code.split('`')[1]}\n\`\`\``;
|
|
||||||
break;
|
|
||||||
case 'js':
|
|
||||||
code = `\`\`\`js\n${story.code}\n\`\`\``;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const codeResult = await codeParser.process(code);
|
|
||||||
const highlightedCode = /** @type {string} */ (codeResult.contents)
|
|
||||||
.replace(/`/g, '\\`')
|
|
||||||
.replace(/\$/g, '\\$');
|
|
||||||
invokeStoriesCode.push(
|
|
||||||
`{ key: '${story.key}', story: ${story.key}, code: \`${highlightedCode}\` }`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fullJsCode = [
|
|
||||||
jsCode,
|
|
||||||
storiesCode,
|
|
||||||
`const rootNode = ${rootNodeQueryCode};`,
|
|
||||||
`const stories = [${invokeStoriesCode.join(', ')}];`,
|
|
||||||
`for (const story of stories) {`,
|
|
||||||
// eslint-disable-next-line no-template-curly-in-string
|
|
||||||
' const storyEl = rootNode.querySelector(`[mdjs-story-name="${story.key}"]`);',
|
|
||||||
` storyEl.codeHasHtml = true;`,
|
|
||||||
` storyEl.story = story.story;`,
|
|
||||||
` storyEl.code = story.code;`,
|
|
||||||
`};`,
|
|
||||||
`if (!customElements.get('mdjs-preview')) { import('@mdjs/mdjs-preview/mdjs-preview.js'); }`,
|
|
||||||
`if (!customElements.get('mdjs-story')) { import('@mdjs/mdjs-story/mdjs-story.js'); }`,
|
|
||||||
].join('\n');
|
|
||||||
}
|
}
|
||||||
return { stories, jsCode: fullJsCode, html: result.contents };
|
|
||||||
|
// @ts-ignore
|
||||||
|
for (const pluginObj of metaPlugins) {
|
||||||
|
if (pluginObj.name === 'mdjsSetupCode') {
|
||||||
|
if (pluginObj.options && !pluginObj.options.highlightCode) {
|
||||||
|
pluginObj.options.highlightCode = highlightCode;
|
||||||
|
}
|
||||||
|
if (!pluginObj.options) {
|
||||||
|
pluginObj.options = { highlightCode };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parser.use(pluginObj.plugin, pluginObj.options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {unknown} */
|
||||||
|
const parseResult = await parser.process(mdjs);
|
||||||
|
const result = /** @type {ParseResult} */ (parseResult);
|
||||||
|
|
||||||
|
const { stories, setupJsCode } = result.data;
|
||||||
|
|
||||||
|
return { stories, jsCode: setupJsCode, html: result.contents };
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
92
packages/mdjs-core/src/mdjsSetupCode.js
Normal file
92
packages/mdjs-core/src/mdjsSetupCode.js
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const slash = require('slash');
|
||||||
|
|
||||||
|
/** @typedef {import('vfile').VFileOptions} VFileOptions */
|
||||||
|
/** @typedef {import('unist').Node} Node */
|
||||||
|
/** @typedef {import('@mdjs/core/types/code').Story} Story */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} simulationSettings
|
||||||
|
* @property {string} [simulatorUrl]
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} rocketConfig
|
||||||
|
* @property {string} [pathPrefix]
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {object} options
|
||||||
|
* @param {string} [options.rootNodeQueryCode]
|
||||||
|
* @param {simulationSettings} [options.simulationSettings]
|
||||||
|
* @param {rocketConfig} [options.rocketConfig]
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function mdjsSetupCode({
|
||||||
|
rootNodeQueryCode = 'document',
|
||||||
|
simulationSettings = {},
|
||||||
|
rocketConfig = {},
|
||||||
|
} = {}) {
|
||||||
|
if (rocketConfig && rocketConfig.pathPrefix) {
|
||||||
|
if (simulationSettings && simulationSettings.simulatorUrl) {
|
||||||
|
const { simulatorUrl } = simulationSettings;
|
||||||
|
if (simulatorUrl[0] === '/' && !simulatorUrl.startsWith(rocketConfig.pathPrefix)) {
|
||||||
|
simulationSettings.simulatorUrl = slash(
|
||||||
|
path.join(rocketConfig.pathPrefix, simulationSettings.simulatorUrl),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Node} tree
|
||||||
|
* @param {VFileOptions} file
|
||||||
|
*/
|
||||||
|
async function transformer(tree, file) {
|
||||||
|
const { stories, jsCode } = file.data;
|
||||||
|
|
||||||
|
file.data.setupJsCode = jsCode;
|
||||||
|
|
||||||
|
if (stories && stories.length > 0) {
|
||||||
|
const storiesCode = stories.map(/** @param {Story} story */ story => story.code).join('\n');
|
||||||
|
|
||||||
|
const invokeStoriesCode = [];
|
||||||
|
for (const story of stories) {
|
||||||
|
invokeStoriesCode.push(`{ key: '${story.key}', story: ${story.key} }`);
|
||||||
|
}
|
||||||
|
|
||||||
|
file.data.setupJsCode = [
|
||||||
|
'/** script code **/',
|
||||||
|
jsCode,
|
||||||
|
'/** stories code **/',
|
||||||
|
storiesCode,
|
||||||
|
'/** stories setup code **/',
|
||||||
|
`const rootNode = ${rootNodeQueryCode};`,
|
||||||
|
`const stories = [${invokeStoriesCode.join(', ')}];`,
|
||||||
|
'let needsMdjsElements = false;',
|
||||||
|
`for (const story of stories) {`,
|
||||||
|
// eslint-disable-next-line no-template-curly-in-string
|
||||||
|
' const storyEl = rootNode.querySelector(`[mdjs-story-name="${story.key}"]`);',
|
||||||
|
' if (storyEl) {',
|
||||||
|
` storyEl.story = story.story;`,
|
||||||
|
` storyEl.key = story.key;`,
|
||||||
|
` needsMdjsElements = true;`,
|
||||||
|
` Object.assign(storyEl, ${JSON.stringify(simulationSettings)});`,
|
||||||
|
' }',
|
||||||
|
`};`,
|
||||||
|
'if (needsMdjsElements) {',
|
||||||
|
` if (!customElements.get('mdjs-preview')) { import('@mdjs/mdjs-preview/define'); }`,
|
||||||
|
` if (!customElements.get('mdjs-story')) { import('@mdjs/mdjs-story/define'); }`,
|
||||||
|
'}',
|
||||||
|
].join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformer;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
mdjsSetupCode,
|
||||||
|
};
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
/** @typedef {import('@mdjs/core/types/code').StoryTypes} StoryTypes */
|
/** @typedef {import('@mdjs/core/types/code').StoryTypes} StoryTypes */
|
||||||
/** @typedef {(name: string) => string} TagFunction */
|
/** @typedef {(name: string) => string} TagFunction */
|
||||||
/** @typedef {import('unist').Node} UnistNode */
|
/** @typedef {import('unist').Node} UnistNode */
|
||||||
|
/** @typedef {import('unist').Parent} UnistParent */
|
||||||
/** @typedef {import('vfile').VFileOptions} VFileOptions */
|
/** @typedef {import('vfile').VFileOptions} VFileOptions */
|
||||||
|
|
||||||
const visit = require('unist-util-visit');
|
const visit = require('unist-util-visit');
|
||||||
@@ -40,7 +41,7 @@ function defaultStoryTag(name) {
|
|||||||
* @param {string} name
|
* @param {string} name
|
||||||
*/
|
*/
|
||||||
function defaultPreviewStoryTag(name) {
|
function defaultPreviewStoryTag(name) {
|
||||||
return `<mdjs-preview mdjs-story-name="${name}"></mdjs-preview>`;
|
return `<mdjs-preview mdjs-story-name="${name}">[[CODE SLOT]]</mdjs-preview>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,14 +56,16 @@ function mdjsStoryParse({
|
|||||||
} = {}) {
|
} = {}) {
|
||||||
/** @type {Story[]} */
|
/** @type {Story[]} */
|
||||||
const stories = [];
|
const stories = [];
|
||||||
let index = 0;
|
let htmlIndex = 0;
|
||||||
|
|
||||||
/* eslint-disable no-param-reassign */
|
/* eslint-disable no-param-reassign */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {UnistNode} node
|
* @param {UnistNode} node
|
||||||
|
* @param {number} index
|
||||||
|
* @param {UnistParent} parent
|
||||||
*/
|
*/
|
||||||
const nodeCodeVisitor = node => {
|
const nodeCodeVisitor = (node, index, parent) => {
|
||||||
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';
|
||||||
@@ -71,30 +74,65 @@ function mdjsStoryParse({
|
|||||||
}
|
}
|
||||||
if (node.lang === 'js' && node.meta === 'preview-story' && typeof node.value === 'string') {
|
if (node.lang === 'js' && node.meta === 'preview-story' && typeof node.value === 'string') {
|
||||||
const storyData = extractStoryData(node.value);
|
const storyData = extractStoryData(node.value);
|
||||||
node.type = 'html';
|
const newValue = previewStoryTag(storyData.name);
|
||||||
node.value = previewStoryTag(storyData.name);
|
if (newValue.includes('[[CODE SLOT]]')) {
|
||||||
|
const tagParts = newValue.split('[[CODE SLOT]]');
|
||||||
|
node = {
|
||||||
|
type: 'root',
|
||||||
|
children: [
|
||||||
|
{ type: 'html', value: tagParts[0] },
|
||||||
|
{ type: 'text', value: '\n\n' },
|
||||||
|
node,
|
||||||
|
{ type: 'text', value: '\n\n' },
|
||||||
|
{ type: 'html', value: tagParts[1] },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
parent.children.splice(index, 1, node);
|
||||||
|
} else {
|
||||||
|
node.type = 'html';
|
||||||
|
node.value = previewStoryTag(storyData.name);
|
||||||
|
}
|
||||||
|
|
||||||
stories.push(storyData);
|
stories.push(storyData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.lang === 'html' && node.meta === 'story') {
|
if (node.lang === 'html' && node.meta === 'story') {
|
||||||
const storyData = extractStoryData(
|
const storyData = extractStoryData(
|
||||||
`export const HtmlStory${index} = () => html\`${node.value}\`;`,
|
`export const HtmlStory${htmlIndex} = () => html\`${node.value}\`;`,
|
||||||
{ type: 'html' },
|
{ type: 'html' },
|
||||||
);
|
);
|
||||||
node.type = 'html';
|
node.type = 'html';
|
||||||
node.value = storyTag(storyData.name);
|
node.value = storyTag(storyData.name);
|
||||||
stories.push(storyData);
|
stories.push(storyData);
|
||||||
index += 1;
|
htmlIndex += 1;
|
||||||
}
|
}
|
||||||
if (node.lang === 'html' && node.meta === 'preview-story') {
|
if (node.lang === 'html' && node.meta === 'preview-story') {
|
||||||
const storyData = extractStoryData(
|
const storyData = extractStoryData(
|
||||||
`export const HtmlStory${index} = () => html\`${node.value}\`;`,
|
`export const HtmlStory${htmlIndex} = () => html\`${node.value}\`;`,
|
||||||
{ type: 'html' },
|
{ type: 'html' },
|
||||||
);
|
);
|
||||||
node.type = 'html';
|
|
||||||
node.value = previewStoryTag(storyData.name);
|
const newValue = previewStoryTag(storyData.name);
|
||||||
|
if (newValue.includes('[[CODE SLOT]]')) {
|
||||||
|
const tagParts = newValue.split('[[CODE SLOT]]');
|
||||||
|
node = {
|
||||||
|
type: 'root',
|
||||||
|
children: [
|
||||||
|
{ type: 'html', value: tagParts[0] },
|
||||||
|
{ type: 'text', value: '\n\n' },
|
||||||
|
node,
|
||||||
|
{ type: 'text', value: '\n\n' },
|
||||||
|
{ type: 'html', value: tagParts[1] },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
parent.children.splice(index, 1, node);
|
||||||
|
} else {
|
||||||
|
node.type = 'html';
|
||||||
|
node.value = previewStoryTag(storyData.name);
|
||||||
|
}
|
||||||
|
|
||||||
stories.push(storyData);
|
stories.push(storyData);
|
||||||
index += 1;
|
htmlIndex += 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,16 @@ describe('Integration', () => {
|
|||||||
'<pre><code class="language-js">const foo = 1;',
|
'<pre><code class="language-js">const foo = 1;',
|
||||||
'</code></pre>',
|
'</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-preview mdjs-story-name="fooPreviewStory">',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'<pre><code class="language-js">export const fooPreviewStory = () => {}',
|
||||||
|
'</code></pre>',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'</mdjs-preview>',
|
||||||
];
|
];
|
||||||
|
|
||||||
const parser = unified()
|
const parser = unified()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/* eslint-disable no-template-curly-in-string */
|
/* eslint-disable no-template-curly-in-string */
|
||||||
|
|
||||||
const chai = require('chai');
|
const chai = require('chai');
|
||||||
|
const { adjustPluginOptions } = require('plugins-manager');
|
||||||
const { mdjsProcess } = require('../src/mdjsProcess.js');
|
const { mdjsProcess } = require('../src/mdjsProcess.js');
|
||||||
|
|
||||||
const { expect } = chai;
|
const { expect } = chai;
|
||||||
@@ -28,27 +29,44 @@ describe('mdjsProcess', () => {
|
|||||||
'<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"><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>',
|
'</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-preview mdjs-story-name="fooPreviewStory">',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'<pre class="language-js"><code class="language-js"><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>',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'</mdjs-preview>',
|
||||||
].join('\n');
|
].join('\n');
|
||||||
const expectedJsCode = [
|
const expectedJsCode = [
|
||||||
|
'/** script code **/',
|
||||||
'const bar = 2;',
|
'const bar = 2;',
|
||||||
|
'/** stories code **/',
|
||||||
'export const fooStory = () => {}',
|
'export const fooStory = () => {}',
|
||||||
'export const fooPreviewStory = () => {}',
|
'export const fooPreviewStory = () => {}',
|
||||||
|
'/** stories setup code **/',
|
||||||
'const rootNode = document;',
|
'const rootNode = document;',
|
||||||
`const stories = [{ key: 'fooStory', story: fooStory, code: \`<pre class="language-js"><code class="language-js"><span class="token keyword module">export</span> <span class="token keyword">const</span> <span class="token function-variable function">fooStory</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>`,
|
`const stories = [{ key: 'fooStory', story: fooStory }, { key: 'fooPreviewStory', story: fooPreviewStory }];`,
|
||||||
`</code></pre>\` }, { key: 'fooPreviewStory', story: fooPreviewStory, code: \`<pre class="language-js"><code class="language-js"><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>`,
|
'let needsMdjsElements = false;',
|
||||||
`</code></pre>\` }];`,
|
|
||||||
'for (const story of stories) {',
|
'for (const story of stories) {',
|
||||||
' const storyEl = rootNode.querySelector(`[mdjs-story-name="${story.key}"]`);',
|
' const storyEl = rootNode.querySelector(`[mdjs-story-name="${story.key}"]`);',
|
||||||
' storyEl.codeHasHtml = true;',
|
' if (storyEl) {',
|
||||||
' storyEl.story = story.story;',
|
' storyEl.story = story.story;',
|
||||||
' storyEl.code = story.code;',
|
' storyEl.key = story.key;',
|
||||||
|
' needsMdjsElements = true;',
|
||||||
|
' Object.assign(storyEl, {});',
|
||||||
|
' }',
|
||||||
'};',
|
'};',
|
||||||
`if (!customElements.get('mdjs-preview')) { import('@mdjs/mdjs-preview/mdjs-preview.js'); }`,
|
'if (needsMdjsElements) {',
|
||||||
`if (!customElements.get('mdjs-story')) { import('@mdjs/mdjs-story/mdjs-story.js'); }`,
|
` if (!customElements.get('mdjs-preview')) { import('@mdjs/mdjs-preview/define'); }`,
|
||||||
|
` if (!customElements.get('mdjs-story')) { import('@mdjs/mdjs-story/define'); }`,
|
||||||
|
'}',
|
||||||
].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);
|
||||||
});
|
});
|
||||||
@@ -150,4 +168,45 @@ describe('mdjsProcess', () => {
|
|||||||
const result = await mdjsProcess(input);
|
const result = await mdjsProcess(input);
|
||||||
expect(result.html.trim()).to.equal(expected);
|
expect(result.html.trim()).to.equal(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can adjust languages for story preview', async () => {
|
||||||
|
const input = [
|
||||||
|
'Intro',
|
||||||
|
'```js preview-story',
|
||||||
|
'export const fooPreviewStory = () => {}',
|
||||||
|
'```',
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
`/** script code **/`,
|
||||||
|
``,
|
||||||
|
`/** stories code **/`,
|
||||||
|
`export const fooPreviewStory = () => {}`,
|
||||||
|
`/** stories setup code **/`,
|
||||||
|
`const rootNode = document;`,
|
||||||
|
`const stories = [{ key: 'fooPreviewStory', story: fooPreviewStory }];`,
|
||||||
|
`let needsMdjsElements = false;`,
|
||||||
|
`for (const story of stories) {`,
|
||||||
|
' const storyEl = rootNode.querySelector(`[mdjs-story-name="${story.key}"]`);',
|
||||||
|
` if (storyEl) {`,
|
||||||
|
` storyEl.story = story.story;`,
|
||||||
|
` storyEl.key = story.key;`,
|
||||||
|
` needsMdjsElements = true;`,
|
||||||
|
' Object.assign(storyEl, {"languages":[{"key":"en","name":"English"}]});',
|
||||||
|
` }`,
|
||||||
|
`};`,
|
||||||
|
`if (needsMdjsElements) {`,
|
||||||
|
` if (!customElements.get('mdjs-preview')) { import('@mdjs/mdjs-preview/define'); }`,
|
||||||
|
` if (!customElements.get('mdjs-story')) { import('@mdjs/mdjs-story/define'); }`,
|
||||||
|
`}`,
|
||||||
|
].join('\n');
|
||||||
|
const result = await mdjsProcess(input, {
|
||||||
|
setupUnifiedPlugins: [
|
||||||
|
adjustPluginOptions('mdjsSetupCode', {
|
||||||
|
simulationSettings: { languages: [{ key: 'en', name: 'English' }] },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
expect(result.jsCode.trim()).to.equal(expected);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ describe('mdjsParse', () => {
|
|||||||
expect(/** @type {MDJSVFileData} */ (result.data).jsCode).to.equal('const bar = 22;');
|
expect(/** @type {MDJSVFileData} */ (result.data).jsCode).to.equal('const bar = 22;');
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: fix this bug
|
// TODO: fix this bug - maybe something in unified itself 🤔
|
||||||
it.skip('handling only "js script" code blocks', async () => {
|
it.skip('handling only "js script" code blocks', async () => {
|
||||||
const input = [
|
const input = [
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -37,9 +37,27 @@ describe('mdjsStoryParse', () => {
|
|||||||
'<pre><code class="language-js">const foo = 1;',
|
'<pre><code class="language-js">const foo = 1;',
|
||||||
'</code></pre>',
|
'</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-preview mdjs-story-name="fooPreviewStory">',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'<pre><code class="language-js">export const fooPreviewStory = () => {}',
|
||||||
|
'</code></pre>',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'</mdjs-preview>',
|
||||||
'<mdjs-story mdjs-story-name="HtmlStory0"></mdjs-story>',
|
'<mdjs-story mdjs-story-name="HtmlStory0"></mdjs-story>',
|
||||||
'<mdjs-preview mdjs-story-name="HtmlStory1"></mdjs-preview>',
|
'<mdjs-preview mdjs-story-name="HtmlStory1">',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'<pre><code class="language-html"><demo-el></demo-el>',
|
||||||
|
'</code></pre>',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'</mdjs-preview>',
|
||||||
'',
|
'',
|
||||||
].join('\n');
|
].join('\n');
|
||||||
|
|
||||||
|
|||||||
1
packages/mdjs-core/types/code.d.ts
vendored
1
packages/mdjs-core/types/code.d.ts
vendored
@@ -25,6 +25,7 @@ export interface ParseResult {
|
|||||||
data: {
|
data: {
|
||||||
stories: Story[];
|
stories: Story[];
|
||||||
jsCode: string;
|
jsCode: string;
|
||||||
|
setupJsCode: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,38 @@
|
|||||||
# @mdjs/mdjs-preview
|
# @mdjs/mdjs-preview
|
||||||
|
|
||||||
|
## 0.4.2
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 72f631a: Improve customizations by hiding empty themes, platforms and adding parts to be styled.
|
||||||
|
- 74dd8d1: Autoheight will not grow bigger than the current size height
|
||||||
|
- 72f631a: Add a copy code button
|
||||||
|
|
||||||
|
## 0.4.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 0f6709a: Make sure initial settings are taken from the element if nothing is yet stored
|
||||||
|
|
||||||
|
## 0.4.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- edb1abf: Reworking completely by
|
||||||
|
|
||||||
|
- slotting in the highlighted code
|
||||||
|
- open story in dedicated window
|
||||||
|
- enabling an simulation mode that can render the story in an iframe
|
||||||
|
- share settings between all simulators
|
||||||
|
- option to remember simulator settings
|
||||||
|
- force side effect import via `/define`
|
||||||
|
|
||||||
|
## 0.3.2
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- be0d0b3: fix: add missing main entry to the packages
|
||||||
|
|
||||||
## 0.3.1
|
## 0.3.1
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@mdjs/mdjs-preview",
|
"name": "@mdjs/mdjs-preview",
|
||||||
"version": "0.3.1",
|
"version": "0.4.2",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
@@ -13,10 +13,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/preview/",
|
"homepage": "https://rocket.modern-web.dev/docs/markdown-javascript/preview/",
|
||||||
|
"main": "./index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./index.js",
|
".": "./index.js",
|
||||||
"./mdjs-preview.js": "./mdjs-preview.js"
|
"./define": "./src/define/define.js"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"debug": "cd ../../ && npm run debug -- --group mdjs-preview",
|
"debug": "cd ../../ && npm run debug -- --group mdjs-preview",
|
||||||
@@ -31,8 +32,8 @@
|
|||||||
"src"
|
"src"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lit-element": "^2.4.0",
|
"@lion/accordion": "^0.4.2",
|
||||||
"lit-html": "^1.3.0"
|
"lit-element": "^2.4.0"
|
||||||
},
|
},
|
||||||
"types": "dist-types/index.d.ts"
|
"types": "dist-types/index.d.ts"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
import { LitElement, html, css } from 'lit-element';
|
import { LitElement, html, css } from 'lit-element';
|
||||||
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
|
import '@lion/accordion/define';
|
||||||
|
|
||||||
|
import {
|
||||||
|
subscribe,
|
||||||
|
unSubscribe,
|
||||||
|
saveToSharedStates,
|
||||||
|
applySharedStates,
|
||||||
|
} from './mdjsViewerSharedStates.js';
|
||||||
|
import { addResizeHandler } from './resizeHandler.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {object} StoryOptions
|
* @typedef {object} StoryOptions
|
||||||
@@ -20,160 +28,801 @@ export class MdJsPreview extends LitElement {
|
|||||||
story: {
|
story: {
|
||||||
attribute: false,
|
attribute: false,
|
||||||
},
|
},
|
||||||
code: {
|
key: {
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
codeHasHtml: {
|
deviceMode: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
},
|
attribute: 'device-mode',
|
||||||
showCode: {
|
|
||||||
type: Boolean,
|
|
||||||
attribute: 'show-code',
|
|
||||||
reflect: true,
|
reflect: true,
|
||||||
},
|
},
|
||||||
|
sameSettings: { type: Boolean },
|
||||||
|
contentHeight: { type: Number },
|
||||||
|
simulatorUrl: { type: String },
|
||||||
|
// page settings
|
||||||
|
platform: { type: String },
|
||||||
|
platforms: { type: Array },
|
||||||
|
size: { type: String },
|
||||||
|
sizes: { type: Array },
|
||||||
|
theme: { type: String, reflect: true },
|
||||||
|
themes: { type: Array },
|
||||||
|
language: { type: String },
|
||||||
|
languages: { type: Array },
|
||||||
|
edgeDistance: { type: Boolean },
|
||||||
|
autoHeight: { type: Boolean },
|
||||||
|
rememberSettings: { type: Boolean },
|
||||||
|
__copyButtonText: { type: String },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleShowCode() {
|
|
||||||
this.showCode = !this.showCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.code = '';
|
|
||||||
/** @type {LitHtmlStoryFn} */
|
/** @type {LitHtmlStoryFn} */
|
||||||
this.story = () => html` <p>Loading...</p> `;
|
this.story = () => html` <p>Loading...</p> `;
|
||||||
this.codeHasHtml = false;
|
this.key = '';
|
||||||
|
this.contentHeight = 0;
|
||||||
|
this.simulatorUrl = '';
|
||||||
|
this.__supportsClipboard = 'clipboard' in navigator;
|
||||||
|
this.__copyButtonText = 'Copy Code';
|
||||||
|
|
||||||
|
this.theme = 'light';
|
||||||
|
/** @type {{ key: string, name: string }[]} */
|
||||||
|
this.themes = [
|
||||||
|
// { key: 'light', name: 'Light' },
|
||||||
|
// { key: 'dark', name: 'Dark' },
|
||||||
|
];
|
||||||
|
|
||||||
|
this.language = 'en-US';
|
||||||
|
this.languages = [
|
||||||
|
{ key: 'en', name: 'English' },
|
||||||
|
{ key: 'en-US', name: 'English (United States)' },
|
||||||
|
{ key: 'en-GB', name: 'English (United Kingdom)' },
|
||||||
|
{ key: 'de', name: 'German' },
|
||||||
|
{ key: 'es', name: 'Spanish' },
|
||||||
|
{ key: 'fi', name: 'Finnish' },
|
||||||
|
{ key: 'fr', name: 'French' },
|
||||||
|
{ key: 'it', name: 'Italian' },
|
||||||
|
{ key: 'nl', name: 'Dutch' },
|
||||||
|
{ key: 'pl', name: 'Polish' },
|
||||||
|
{ key: 'pt', name: 'Portuguese' },
|
||||||
|
{ key: 'ro', name: 'Romanian' },
|
||||||
|
{ key: 'sv', name: 'Swedish' },
|
||||||
|
];
|
||||||
|
|
||||||
|
this.platform = 'web';
|
||||||
|
|
||||||
|
/** @type {{ key: string, name: string }[]} */
|
||||||
|
this.platforms = [
|
||||||
|
// { key: 'web', name: 'Web' },
|
||||||
|
// { key: 'web-windows', name: 'Windows' },
|
||||||
|
// { key: 'web-mac', name: 'Mac' },
|
||||||
|
// { key: 'android', name: 'Android' },
|
||||||
|
// { key: 'ios', name: 'iOS' },
|
||||||
|
];
|
||||||
|
|
||||||
|
this.size = 'webSmall';
|
||||||
|
this.sizes = [
|
||||||
|
{
|
||||||
|
key: 'webSmall',
|
||||||
|
name: 'Small',
|
||||||
|
platform: 'web',
|
||||||
|
width: 360,
|
||||||
|
height: 640,
|
||||||
|
dpr: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'webMedium',
|
||||||
|
name: 'Medium',
|
||||||
|
platform: 'web',
|
||||||
|
width: 640,
|
||||||
|
height: 640,
|
||||||
|
dpr: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'webLarge',
|
||||||
|
name: 'Large',
|
||||||
|
platform: 'web',
|
||||||
|
width: 1024,
|
||||||
|
height: 640,
|
||||||
|
dpr: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'pixel2',
|
||||||
|
name: 'Pixel 2',
|
||||||
|
platform: 'android',
|
||||||
|
width: 411,
|
||||||
|
height: 731,
|
||||||
|
dpr: 2.6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'galaxyS5',
|
||||||
|
name: 'Galaxy S5',
|
||||||
|
platform: 'android',
|
||||||
|
width: 360,
|
||||||
|
height: 640,
|
||||||
|
dpr: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'iphoneX',
|
||||||
|
name: 'iPhone X',
|
||||||
|
platform: 'ios',
|
||||||
|
width: 375,
|
||||||
|
height: 812,
|
||||||
|
dpr: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'iPad',
|
||||||
|
name: 'iPad',
|
||||||
|
platform: 'ios',
|
||||||
|
width: 768,
|
||||||
|
height: 1024,
|
||||||
|
dpr: 2,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
this.deviceMode = false;
|
||||||
|
this.autoHeight = true;
|
||||||
|
this.edgeDistance = true;
|
||||||
|
this.sameSettings = true;
|
||||||
|
this.rememberSettings = false;
|
||||||
|
|
||||||
|
this.__firstRun = true;
|
||||||
|
this.__syncUp = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
if (this.sameSettings) {
|
||||||
|
applySharedStates(this);
|
||||||
|
}
|
||||||
|
addResizeHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
get baseUrl() {
|
||||||
|
return document.location.origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
get deviceHeight() {
|
||||||
|
const maxHeight = this.sizeData?.height || 50;
|
||||||
|
if (this.autoHeight) {
|
||||||
|
return Math.min(this.contentHeight, maxHeight);
|
||||||
|
}
|
||||||
|
return maxHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} platform
|
||||||
|
*/
|
||||||
|
getSizesFor(platform) {
|
||||||
|
return this.sizes.filter(el => el.platform === platform);
|
||||||
|
}
|
||||||
|
|
||||||
|
get sizeData() {
|
||||||
|
return (
|
||||||
|
this.sizes.find(el => el.key === this.size) || { width: 50, height: 50, name: 'default' }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubscribe = () => {
|
||||||
|
this.__syncUp = false;
|
||||||
|
applySharedStates(this);
|
||||||
|
this.__syncUp = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('lit-element').PropertyValues} changeProps
|
||||||
|
*/
|
||||||
|
update(changeProps) {
|
||||||
|
super.update(changeProps);
|
||||||
|
if (this.sameSettings && this.__syncUp) {
|
||||||
|
saveToSharedStates(this, this.onSubscribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changeProps.has('sameSettings')) {
|
||||||
|
if (this.sameSettings) {
|
||||||
|
subscribe(this.onSubscribe);
|
||||||
|
} else {
|
||||||
|
unSubscribe(this.onSubscribe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
if (this.sameSettings) {
|
||||||
|
unSubscribe(this.onSubscribe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
this.__syncUp = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
get iframeUrl() {
|
||||||
|
const mdjsSetupScript = /** @type {HTMLScriptElement} */ (document.querySelector(
|
||||||
|
'script[type=module][mdjs-setup]',
|
||||||
|
));
|
||||||
|
if (!mdjsSetupScript) {
|
||||||
|
throw new Error('Could not find a <script type="module" src="..." mdjs-setup></script>');
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.set('story-file', mdjsSetupScript.src);
|
||||||
|
params.set('story-key', this.key);
|
||||||
|
params.set('theme', this.theme);
|
||||||
|
params.set('platform', this.platform);
|
||||||
|
params.set('language', this.language);
|
||||||
|
params.set('edge-distance', this.edgeDistance.toString());
|
||||||
|
|
||||||
|
const links = /** @type {HTMLLinkElement[]} */ ([
|
||||||
|
...document.querySelectorAll('link[mdjs-use]'),
|
||||||
|
]);
|
||||||
|
for (const link of links) {
|
||||||
|
if (link.href) {
|
||||||
|
params.append('stylesheets', link.href);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${this.simulatorUrl}#?${params.toString()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} platform
|
||||||
|
*/
|
||||||
|
changePlatform(platform) {
|
||||||
|
this.platform = platform;
|
||||||
|
const sizes = this.getSizesFor(this.platform);
|
||||||
|
this.size = sizes[0].key;
|
||||||
|
}
|
||||||
|
|
||||||
|
async onCopy() {
|
||||||
|
if (this.textContent) {
|
||||||
|
await navigator.clipboard.writeText(this.textContent.trim());
|
||||||
|
this.__copyButtonText = 'Copied ✅';
|
||||||
|
setTimeout(() => {
|
||||||
|
this.__copyButtonText = 'Copy code';
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPlatforms() {
|
||||||
|
if (this.platforms.length) {
|
||||||
|
return html`
|
||||||
|
<h4>Platform</h4>
|
||||||
|
<div
|
||||||
|
class="segmented-control"
|
||||||
|
@change=${
|
||||||
|
/** @param {Event} ev */ ev => {
|
||||||
|
if (ev.target) {
|
||||||
|
this.changePlatform(/** @type {HTMLInputElement} */ (ev.target).value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
${this.platforms.map(
|
||||||
|
platform => html`
|
||||||
|
<label class="${this.platform === platform.key ? 'selected' : ''}">
|
||||||
|
<span>${platform.name}</span>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="platform"
|
||||||
|
value="${platform.key}"
|
||||||
|
?checked=${this.platform === platform.key}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
`,
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPlatform() {
|
||||||
|
if (this.platforms.length) {
|
||||||
|
return html`
|
||||||
|
<div>
|
||||||
|
<h3>Platform</h3>
|
||||||
|
${this.renderPlatforms()}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSizes() {
|
||||||
|
if (this.sizes.length) {
|
||||||
|
return html`
|
||||||
|
<h4>Size</h4>
|
||||||
|
<div
|
||||||
|
class="segmented-control"
|
||||||
|
@change=${
|
||||||
|
/** @param {Event} ev */ ev => {
|
||||||
|
if (ev.target) {
|
||||||
|
this.size = /** @type {HTMLInputElement} */ (ev.target).value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
${this.getSizesFor(this.platform).map(
|
||||||
|
size => html`
|
||||||
|
<label class="${this.size === size.key ? 'selected' : ''}">
|
||||||
|
<span>${size.name}</span>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="size"
|
||||||
|
value="${size.key}"
|
||||||
|
.checked=${this.size === size.key}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
`,
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderViewport() {
|
||||||
|
return html`
|
||||||
|
<div>
|
||||||
|
<h3>Viewport</h3>
|
||||||
|
${this.renderSizes()} ${this.renderAutoHeight()}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderThemes() {
|
||||||
|
if (this.themes.length) {
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
class="segmented-control"
|
||||||
|
@change=${
|
||||||
|
/** @param {Event} ev */ ev => {
|
||||||
|
if (ev.target) {
|
||||||
|
this.theme = /** @type {HTMLInputElement} */ (ev.target).value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
${this.themes.map(
|
||||||
|
theme => html`
|
||||||
|
<label class="${this.theme === theme.key ? 'selected' : ''}">
|
||||||
|
<span>${theme.name}</span>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="theme"
|
||||||
|
value="${theme.key}"
|
||||||
|
?checked=${this.theme === theme.key}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
`,
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderVisual() {
|
||||||
|
return html`
|
||||||
|
<div>
|
||||||
|
<h3>Visual</h3>
|
||||||
|
${this.renderThemes()} ${this.renderEdgeDistance()}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderLanguages() {
|
||||||
|
if (this.languages.length) {
|
||||||
|
return html`
|
||||||
|
<label>
|
||||||
|
Language
|
||||||
|
<select
|
||||||
|
@change=${
|
||||||
|
/** @param {Event} ev */ ev => {
|
||||||
|
if (ev.target) {
|
||||||
|
this.language = /** @type {HTMLInputElement} */ (ev.target).value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
${this.languages.map(
|
||||||
|
language => html`
|
||||||
|
<option value="${language.key}" ?selected=${this.language === language.key}>
|
||||||
|
${language.name}
|
||||||
|
</option>
|
||||||
|
`,
|
||||||
|
)}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderLocalization() {
|
||||||
|
return html`
|
||||||
|
<div>
|
||||||
|
<h3>Localization</h3>
|
||||||
|
${this.renderLanguages()}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderEdgeDistance() {
|
||||||
|
return html`
|
||||||
|
<div>
|
||||||
|
<label class="${this.edgeDistance ? 'switch selected' : 'switch'}">
|
||||||
|
Apply distance to edge
|
||||||
|
<span part="switch-button"></span>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
?checked=${this.edgeDistance}
|
||||||
|
@change=${
|
||||||
|
/** @param {Event} ev */ ev => {
|
||||||
|
if (ev.target) {
|
||||||
|
this.edgeDistance = /** @type {HTMLInputElement} */ (ev.target).checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAutoHeight() {
|
||||||
|
return html`
|
||||||
|
<div>
|
||||||
|
<label class="${this.autoHeight ? 'switch selected' : 'switch'}">
|
||||||
|
Fit height to content
|
||||||
|
<span part="switch-button"></span>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
?checked=${this.autoHeight}
|
||||||
|
@change=${
|
||||||
|
/** @param {Event} ev */ ev => {
|
||||||
|
if (ev.target) {
|
||||||
|
this.autoHeight = /** @type {HTMLInputElement} */ (ev.target).checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSameSettings() {
|
||||||
|
return html`
|
||||||
|
<div>
|
||||||
|
<label class="${this.sameSettings ? 'switch selected' : 'switch'}">
|
||||||
|
Same settings for all simulations
|
||||||
|
<span part="switch-button"></span>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
?checked=${this.sameSettings}
|
||||||
|
@change=${
|
||||||
|
/** @param {Event} ev */ ev => {
|
||||||
|
if (ev.target) {
|
||||||
|
this.sameSettings = /** @type {HTMLInputElement} */ (ev.target).checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderRememberSettings() {
|
||||||
|
if (!this.sameSettings) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<div>
|
||||||
|
<label class="${this.rememberSettings ? 'switch selected' : 'switch'}">
|
||||||
|
Remember settings
|
||||||
|
<span part="switch-button"></span>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
?checked=${this.rememberSettings}
|
||||||
|
@change=${
|
||||||
|
/** @param {Event} ev */ ev => {
|
||||||
|
if (ev.target) {
|
||||||
|
this.rememberSettings = /** @type {HTMLInputElement} */ (ev.target).checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSyncSettings() {
|
||||||
|
return html`
|
||||||
|
<div>
|
||||||
|
<h3>Global</h3>
|
||||||
|
${this.renderSameSettings()} ${this.renderRememberSettings()}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
<div>${this.story({ shadowRoot: this.shadowRoot })}</div>
|
${this.deviceMode === false
|
||||||
<button id="showCodeButton" @click=${this.toggleShowCode}>show code</button>
|
? html`<div>${this.story({ shadowRoot: this.shadowRoot })}</div>`
|
||||||
|
: html`
|
||||||
|
<iframe
|
||||||
|
part="iframe"
|
||||||
|
csp=${`script-src ${document.location.origin} 'unsafe-inline'; connect-src ws://${document.location.host}/`}
|
||||||
|
.src=${this.iframeUrl}
|
||||||
|
style=${`width: ${this.sizeData.width}px; height: ${this.deviceHeight}px;`}
|
||||||
|
></iframe>
|
||||||
|
<p part="frame-description" style=${`width: ${this.sizeData.width + 4}px;`}>
|
||||||
|
${this.sizeData.name} - ${this.deviceHeight}x${this.sizeData.width}
|
||||||
|
</p>
|
||||||
|
`}
|
||||||
</div>
|
</div>
|
||||||
${this.codeHasHtml ? unsafeHTML(this.code) : html`<pre><code>${this.code}</code></pre>`}
|
<lion-accordion class="options">
|
||||||
|
${this.deviceMode
|
||||||
|
? html`
|
||||||
|
<h3 slot="invoker">
|
||||||
|
<button>Settings</button>
|
||||||
|
</h3>
|
||||||
|
<div slot="content">
|
||||||
|
<div class="settings-wrapper">
|
||||||
|
${this.renderPlatform()} ${this.renderViewport()} ${this.renderVisual()}
|
||||||
|
${this.renderLocalization()} ${this.renderSyncSettings()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ''}
|
||||||
|
<h3 slot="invoker">
|
||||||
|
<button>Code</button>
|
||||||
|
</h3>
|
||||||
|
<div slot="content">
|
||||||
|
<slot id="code-slot"></slot>
|
||||||
|
<button part="copy-button" @click="${this.onCopy}" ?hidden="${!this.__supportsClipboard}">
|
||||||
|
${this.__copyButtonText}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</lion-accordion>
|
||||||
|
${this.simulatorUrl
|
||||||
|
? html`
|
||||||
|
<div class="controls">
|
||||||
|
<a href=${this.iframeUrl} target="_blank">Open simulation in new window</a>
|
||||||
|
<button
|
||||||
|
@click=${() => (this.deviceMode = !this.deviceMode)}
|
||||||
|
class="simulation-toggle"
|
||||||
|
>
|
||||||
|
${this.deviceMode ? html`Disable` : html`Enable`} device simulation
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ''}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return css`
|
||||||
:host {
|
:host {
|
||||||
border: 1px solid #ccc;
|
|
||||||
display: block;
|
display: block;
|
||||||
box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px;
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#showCodeButton {
|
iframe {
|
||||||
position: absolute;
|
border: 2px solid #4caf50;
|
||||||
right: 5px;
|
background: #fff;
|
||||||
bottom: 5px;
|
}
|
||||||
|
|
||||||
|
[part='copy-button'] {
|
||||||
|
border: 1px solid var(--primary-color, #3f51b5);
|
||||||
|
border-radius: 9px;
|
||||||
|
padding: 7px;
|
||||||
|
background: none;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--primary-color, #3f51b5);
|
||||||
|
text-align: center;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 20px;
|
line-height: 12px;
|
||||||
color: #24292e;
|
float: right;
|
||||||
background-color: #eff3f6;
|
margin-top: -10px;
|
||||||
background-image: linear-gradient(-180deg, #fafbfc, #eff3f6 90%);
|
}
|
||||||
|
|
||||||
|
[part='copy-button']:hover {
|
||||||
|
background-color: var(--primary-color, #3f51b5);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch:focus-within [part='switch-button'] {
|
||||||
|
box-shadow: 0 0 0 1px hsl(0deg 0% 100% / 40%), 0 0 0 4px rgb(31 117 203 / 48%);
|
||||||
|
}
|
||||||
|
|
||||||
|
[part='switch-button'] {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 3px 12px;
|
width: 44px;
|
||||||
white-space: nowrap;
|
background: #808080;
|
||||||
vertical-align: middle;
|
height: 25px;
|
||||||
cursor: pointer;
|
border-radius: 15px;
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
background-repeat: repeat-x;
|
|
||||||
background-position: -1px -1px;
|
|
||||||
background-size: 110% 110%;
|
|
||||||
border: 1px solid rgba(27, 31, 35, 0.2);
|
|
||||||
border-radius: 0.25em;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
-moz-appearance: none;
|
|
||||||
appearance: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#showCodeButton:hover {
|
|
||||||
background-color: #e6ebf1;
|
|
||||||
background-image: linear-gradient(-180deg, #f0f3f6, #e6ebf1 90%);
|
|
||||||
background-position: -0.5em;
|
|
||||||
border-color: rgba(27, 31, 35, 0.35);
|
|
||||||
text-decoration: none;
|
|
||||||
background-repeat: repeat-x;
|
|
||||||
}
|
|
||||||
|
|
||||||
#wrapper {
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
[part='switch-button']::after {
|
||||||
|
content: ' ';
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: rgb(255, 255, 255);
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 3px;
|
||||||
|
left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch.selected [part='switch-button'] {
|
||||||
|
background: var(--primary-color, #008000);
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch.selected [part='switch-button']::after {
|
||||||
|
left: auto;
|
||||||
|
right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[part='frame-description'] {
|
||||||
|
margin: -5px 0 10px 0;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-wrapper {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
grid-gap: 20px 40px;
|
||||||
|
max-width: 650px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 640px) {
|
||||||
|
.settings-wrapper {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-wrapper h3 {
|
||||||
|
margin: 10px 0;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options {
|
||||||
|
display: block;
|
||||||
|
padding: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls a {
|
||||||
|
color: var(--primary-color, #3f51b5);
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 37px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simulation-toggle {
|
||||||
|
border: 1px solid var(--primary-color, #3f51b5);
|
||||||
|
border-radius: 9px;
|
||||||
|
padding: 10px;
|
||||||
|
background: none;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--primary-color, #3f51b5);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simulation-toggle:hover {
|
||||||
|
background-color: var(--primary-color, #3f51b5);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3[slot='invoker'] button {
|
||||||
|
font-size: 16px;
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
padding: 10px;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid #bbb;
|
||||||
|
width: 100%;
|
||||||
|
background: none;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3[slot='invoker'] button::after {
|
||||||
|
content: '>';
|
||||||
|
right: 20px;
|
||||||
|
top: 10px;
|
||||||
|
position: absolute;
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
h3[slot='invoker'][expanded='true'] button::after {
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
h3[slot='invoker'][expanded='true'] button {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[slot='content'] {
|
||||||
|
border-bottom: 1px solid #bbb;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host > pre {
|
h3[slot='invoker']:first-child button {
|
||||||
display: none;
|
border-top: 1px solid #bbb;
|
||||||
margin: 0;
|
|
||||||
padding: 16px;
|
|
||||||
overflow: auto;
|
|
||||||
font-size: 85%;
|
|
||||||
line-height: 1.45;
|
|
||||||
background-color: #f6f8fa;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([show-code]) > pre {
|
h4 {
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.segmented-control {
|
||||||
|
border: 1px solid var(--primary-color, #3f51b5);
|
||||||
|
border-radius: 18px;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.segmented-control span {
|
||||||
|
padding: 5px 10px;
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 18px;
|
||||||
|
margin: 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.segmented-control label:first-child span {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.segmented-control label:last-child span {
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.segmented-control label.selected span {
|
||||||
|
background: var(--primary-color, #3f51b5);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.segmented-control label:focus-within span {
|
||||||
|
box-shadow: 0 0 0 1px hsl(0deg 0% 100% / 40%), 0 0 0 4px rgb(31 117 203 / 48%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.segmented-control input,
|
||||||
|
.switch input {
|
||||||
|
clip: rect(0 0 0 0);
|
||||||
|
clip-path: inset(50%);
|
||||||
|
height: 1px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
padding: 5px;
|
||||||
|
border: 1px solid #333;
|
||||||
code[class*='language-'],
|
|
||||||
pre[class*='language-'] {
|
|
||||||
color: #393a34;
|
|
||||||
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
|
||||||
direction: ltr;
|
|
||||||
text-align: left;
|
|
||||||
white-space: pre;
|
|
||||||
word-spacing: normal;
|
|
||||||
word-break: normal;
|
|
||||||
tab-size: 4;
|
|
||||||
hyphens: none;
|
|
||||||
overflow: auto;
|
|
||||||
font-size: 85%;
|
|
||||||
line-height: 1.45;
|
|
||||||
background-color: #f6f8fa;
|
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.token.function,
|
|
||||||
.token.class-name {
|
|
||||||
color: #6f42c1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.tag,
|
|
||||||
.token.selector,
|
|
||||||
.language-autohotkey .token.keyword {
|
|
||||||
color: #22863a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.entity,
|
|
||||||
.token.url,
|
|
||||||
.token.symbol,
|
|
||||||
.token.number,
|
|
||||||
.token.boolean,
|
|
||||||
.token.variable,
|
|
||||||
.token.constant,
|
|
||||||
.token.property,
|
|
||||||
.token.inserted,
|
|
||||||
.token.punctuation,
|
|
||||||
.token.operator {
|
|
||||||
color: #005cc5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.regex {
|
|
||||||
color: #032f62;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.atrule,
|
|
||||||
.token.keyword,
|
|
||||||
.token.attr-name,
|
|
||||||
.language-autohotkey .token.selector {
|
|
||||||
color: #d73a49;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
import { MdJsPreview } from './src/MdJsPreview.js';
|
import { MdJsPreview } from '../MdJsPreview.js';
|
||||||
|
|
||||||
customElements.define('mdjs-preview', MdJsPreview);
|
customElements.define('mdjs-preview', MdJsPreview);
|
||||||
132
packages/mdjs-preview/src/mdjsViewerSharedStates.js
Normal file
132
packages/mdjs-preview/src/mdjsViewerSharedStates.js
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
const _sharedStates = {
|
||||||
|
platform: 'web',
|
||||||
|
size: 'webSmall',
|
||||||
|
theme: 'light',
|
||||||
|
language: 'en',
|
||||||
|
autoHeight: true,
|
||||||
|
deviceMode: false,
|
||||||
|
rememberSettings: false,
|
||||||
|
edgeDistance: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @type {Function[]} */
|
||||||
|
let subscribeFns = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Function} subscribeFn
|
||||||
|
*/
|
||||||
|
export function subscribe(subscribeFn) {
|
||||||
|
subscribeFns.push(subscribeFn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Function} subscribeFn
|
||||||
|
*/
|
||||||
|
export function unSubscribe(subscribeFn) {
|
||||||
|
subscribeFns = subscribeFns.filter(fn => fn !== subscribeFn);
|
||||||
|
}
|
||||||
|
|
||||||
|
function storeSettings() {
|
||||||
|
for (const _sharedStateKey of Object.keys(_sharedStates)) {
|
||||||
|
const sharedStateKey = /** @type {keyof _sharedStates} */ (_sharedStateKey);
|
||||||
|
if (_sharedStates.rememberSettings) {
|
||||||
|
localStorage.setItem(
|
||||||
|
`mdjsViewerSharedStates-${sharedStateKey}`,
|
||||||
|
_sharedStates[sharedStateKey].toString(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem(`mdjsViewerSharedStates-${sharedStateKey}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let hasGlobalStateBeenSetBefore = false;
|
||||||
|
|
||||||
|
function restoreSettings() {
|
||||||
|
for (const _sharedStateKey of Object.keys(_sharedStates)) {
|
||||||
|
const sharedStateKey = /** @type {keyof _sharedStates} */ (_sharedStateKey);
|
||||||
|
const restoredValue = localStorage.getItem(`mdjsViewerSharedStates-${sharedStateKey}`);
|
||||||
|
if (restoredValue !== null) {
|
||||||
|
switch (sharedStateKey) {
|
||||||
|
case 'autoHeight':
|
||||||
|
case 'deviceMode':
|
||||||
|
case 'rememberSettings':
|
||||||
|
case 'edgeDistance':
|
||||||
|
_sharedStates[sharedStateKey] = restoredValue === 'true' ? true : false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_sharedStates[sharedStateKey] = restoredValue;
|
||||||
|
}
|
||||||
|
hasGlobalStateBeenSetBefore = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreSettings();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('./MdJsPreview.js').MdJsPreview} target
|
||||||
|
*/
|
||||||
|
export function applySharedStates(target) {
|
||||||
|
if (hasGlobalStateBeenSetBefore) {
|
||||||
|
for (const _sharedStateKey of Object.keys(_sharedStates)) {
|
||||||
|
const sharedStateKey = /** @type {keyof _sharedStates} */ (_sharedStateKey);
|
||||||
|
switch (sharedStateKey) {
|
||||||
|
case 'autoHeight':
|
||||||
|
case 'deviceMode':
|
||||||
|
case 'rememberSettings':
|
||||||
|
case 'edgeDistance':
|
||||||
|
target[sharedStateKey] = _sharedStates[sharedStateKey];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
target[sharedStateKey] = _sharedStates[sharedStateKey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_saveToSharedStates(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('./MdJsPreview.js').MdJsPreview} target
|
||||||
|
*/
|
||||||
|
function _saveToSharedStates(target) {
|
||||||
|
let updated = false;
|
||||||
|
for (const _sharedStateKey of Object.keys(_sharedStates)) {
|
||||||
|
const sharedStateKey = /** @type {keyof _sharedStates} */ (_sharedStateKey);
|
||||||
|
if (_sharedStates[sharedStateKey] !== target[sharedStateKey]) {
|
||||||
|
switch (sharedStateKey) {
|
||||||
|
case 'autoHeight':
|
||||||
|
case 'deviceMode':
|
||||||
|
case 'rememberSettings':
|
||||||
|
case 'edgeDistance':
|
||||||
|
_sharedStates[sharedStateKey] = target[sharedStateKey];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_sharedStates[sharedStateKey] = target[sharedStateKey];
|
||||||
|
}
|
||||||
|
updated = true;
|
||||||
|
hasGlobalStateBeenSetBefore = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('./MdJsPreview.js').MdJsPreview} target
|
||||||
|
* @param {Function} subscribedFn
|
||||||
|
*/
|
||||||
|
export function saveToSharedStates(target, subscribedFn) {
|
||||||
|
const updated = _saveToSharedStates(target);
|
||||||
|
if (updated) {
|
||||||
|
storeSettings();
|
||||||
|
for (const subscribeFn of subscribeFns) {
|
||||||
|
if (subscribeFn !== subscribedFn) {
|
||||||
|
subscribeFn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
packages/mdjs-preview/src/resizeHandler.js
Normal file
19
packages/mdjs-preview/src/resizeHandler.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
let addedCount = 0;
|
||||||
|
|
||||||
|
export function addResizeHandler() {
|
||||||
|
if (addedCount > 0) {
|
||||||
|
addedCount += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.addEventListener('message', ev => {
|
||||||
|
const data = JSON.parse(ev.data);
|
||||||
|
if (data.action === 'mdjs-viewer-resize') {
|
||||||
|
const viewer = /** @type {import('./MdJsPreview.js').MdJsPreview} */ (document.body.querySelector(
|
||||||
|
`[mdjs-story-name="${data.storyKey}"]`,
|
||||||
|
));
|
||||||
|
if (viewer) {
|
||||||
|
viewer.contentHeight = data.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
38
packages/mdjs-preview/test-web/mdjs-preview.test.js
Normal file
38
packages/mdjs-preview/test-web/mdjs-preview.test.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { expect, fixture, html } from '@open-wc/testing';
|
||||||
|
import '@mdjs/mdjs-preview/define';
|
||||||
|
|
||||||
|
/** @typedef {import('@mdjs/mdjs-preview').MdJsPreview} MdJsPreview */
|
||||||
|
|
||||||
|
describe('mdjs-preview', () => {
|
||||||
|
it('will render the element into the shadow root by default', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<mdjs-preview .story=${() => html`<p id="testing"></p>`}></mdjs-preview>
|
||||||
|
`);
|
||||||
|
expect(el.shadowRoot.querySelectorAll('#testing').length).to.equal(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sync simulator states between instances', async () => {
|
||||||
|
const el = await fixture(html`
|
||||||
|
<div>
|
||||||
|
<mdjs-preview .story=${() => html`<p></p>`}></mdjs-preview>
|
||||||
|
<mdjs-preview .story=${() => html`<p></p>`}></mdjs-preview>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
const [preview1, preview2] = /** @type {MdJsPreview[]} */ (el.children);
|
||||||
|
preview1.platform = 'web';
|
||||||
|
preview1.edgeDistance = true;
|
||||||
|
await preview1.updateComplete;
|
||||||
|
expect(preview1.platform).to.equal('web');
|
||||||
|
expect(preview2.platform).to.equal('web');
|
||||||
|
expect(preview1.edgeDistance).to.be.true;
|
||||||
|
expect(preview2.edgeDistance).to.be.true;
|
||||||
|
|
||||||
|
preview1.platform = 'android';
|
||||||
|
preview1.edgeDistance = false;
|
||||||
|
await preview1.updateComplete;
|
||||||
|
expect(preview1.platform).to.equal('android');
|
||||||
|
expect(preview2.platform).to.equal('android');
|
||||||
|
expect(preview1.edgeDistance).to.be.false;
|
||||||
|
expect(preview2.edgeDistance).to.be.false;
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,5 +1,17 @@
|
|||||||
# @mdjs/mdjs-story
|
# @mdjs/mdjs-story
|
||||||
|
|
||||||
|
## 0.2.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- 604a80e: Force `/define`Ï entrypoint via export map
|
||||||
|
|
||||||
|
## 0.1.2
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- be0d0b3: fix: add missing main entry to the packages
|
||||||
|
|
||||||
## 0.1.1
|
## 0.1.1
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@mdjs/mdjs-story",
|
"name": "@mdjs/mdjs-story",
|
||||||
"version": "0.1.1",
|
"version": "0.2.0",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
@@ -13,10 +13,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/story/",
|
"homepage": "https://rocket.modern-web.dev/docs/markdown-javascript/story/",
|
||||||
|
"main": "./index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./index.js",
|
".": "./index.js",
|
||||||
"./mdjs-story.js": "./mdjs-story.js"
|
"./define": "./src/define.js"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"debug": "cd ../../ && npm run debug -- --group mdjs-story",
|
"debug": "cd ../../ && npm run debug -- --group mdjs-story",
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
import { MdJsStory } from './src/MdJsStory.js';
|
import { MdJsStory } from './MdJsStory.js';
|
||||||
|
|
||||||
customElements.define('mdjs-story', MdJsStory);
|
customElements.define('mdjs-story', MdJsStory);
|
||||||
@@ -1,5 +1,19 @@
|
|||||||
# @rocket/navigation
|
# @rocket/navigation
|
||||||
|
|
||||||
|
## 0.2.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 728a205: feat(navigation): add no-redirects attribute
|
||||||
|
|
||||||
|
By default, the sidebar nav redirects clicks on category headings to
|
||||||
|
their first child.
|
||||||
|
|
||||||
|
To disable those redirects, override
|
||||||
|
\_includes/\_joiningBlocks/\_layoutSidebar/sidebar/20-navigation.njk
|
||||||
|
and add the no-redirects attribute to the <rocket-navigation>
|
||||||
|
element.
|
||||||
|
|
||||||
## 0.2.0
|
## 0.2.0
|
||||||
|
|
||||||
### Minor Changes
|
### Minor Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@rocket/navigation",
|
"name": "@rocket/navigation",
|
||||||
"version": "0.2.0",
|
"version": "0.2.1",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,3 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Debounce a function
|
||||||
|
* @template {(this: any, ...args: any[]) => void} T
|
||||||
|
* @param {T} func function
|
||||||
|
* @param {number} wait time in milliseconds to debounce
|
||||||
|
* @param {boolean} immediate when true, run immediately and on the leading edge
|
||||||
|
* @return {T} debounced function
|
||||||
|
*/
|
||||||
|
function debounce(func, wait, immediate) {
|
||||||
|
/** @type {number|undefined} */
|
||||||
|
let timeout;
|
||||||
|
return /** @type {typeof func}*/ (function () {
|
||||||
|
let args = /** @type {Parameters<typeof func>} */ (/** @type {unknown}*/ (arguments));
|
||||||
|
const later = () => {
|
||||||
|
timeout = undefined;
|
||||||
|
if (!immediate) func.apply(this, args);
|
||||||
|
};
|
||||||
|
const callNow = immediate && !timeout;
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(later, wait);
|
||||||
|
if (callNow) func.apply(this, args);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {object} NavigationListItem
|
* @typedef {object} NavigationListItem
|
||||||
* @property {HTMLElement} headline
|
* @property {HTMLElement} headline
|
||||||
@@ -5,12 +29,17 @@
|
|||||||
* @property {number} top
|
* @property {number} top
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @element rocket-navigation
|
||||||
|
* @attr {Boolean} no-redirects - if set, will not redirect to first child of nav category when clicking on category header.
|
||||||
|
*/
|
||||||
export class RocketNavigation extends HTMLElement {
|
export class RocketNavigation extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
/** @type NavigationListItem[] */
|
/** @type NavigationListItem[] */
|
||||||
this.list = [];
|
this.list = [];
|
||||||
this.__scrollHandler = this.__scrollHandler.bind(this);
|
this.__clickHandler = this.__clickHandler.bind(this);
|
||||||
|
this.__scrollHandler = debounce(this.__scrollHandler.bind(this), 25, true);
|
||||||
this.__isSetup = false;
|
this.__isSetup = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,27 +49,7 @@ export class RocketNavigation extends HTMLElement {
|
|||||||
}
|
}
|
||||||
this.__isSetup = true;
|
this.__isSetup = true;
|
||||||
|
|
||||||
this.addEventListener('click', ev => {
|
this.addEventListener('click', this.__clickHandler);
|
||||||
const el = /** @type {HTMLElement} */ (ev.target);
|
|
||||||
if (el.classList.contains('anchor')) {
|
|
||||||
const anchor = /** @type {HTMLAnchorElement} */ (el);
|
|
||||||
ev.preventDefault();
|
|
||||||
this.dispatchEvent(new Event('close-overlay', { bubbles: true }));
|
|
||||||
// wait for closing animation to finish before start scrolling
|
|
||||||
setTimeout(() => {
|
|
||||||
const parsedUrl = new URL(anchor.href);
|
|
||||||
document.location.hash = parsedUrl.hash;
|
|
||||||
}, 250);
|
|
||||||
}
|
|
||||||
const links = el.parentElement?.querySelectorAll('ul a');
|
|
||||||
if (links && links.length > 1) {
|
|
||||||
const subLink = /** @type {HTMLAnchorElement} */ (links[1]);
|
|
||||||
if (!subLink.classList.contains('anchor')) {
|
|
||||||
ev.preventDefault();
|
|
||||||
subLink.click();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const anchors = /** @type {NodeListOf<HTMLAnchorElement>} */ (this.querySelectorAll(
|
const anchors = /** @type {NodeListOf<HTMLAnchorElement>} */ (this.querySelectorAll(
|
||||||
'li.current a.anchor',
|
'li.current a.anchor',
|
||||||
@@ -57,12 +66,41 @@ export class RocketNavigation extends HTMLElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: debounce
|
window.addEventListener('scroll', this.__scrollHandler, { passive: true });
|
||||||
window.addEventListener('scroll', this.__scrollHandler);
|
|
||||||
|
|
||||||
this.__scrollHandler();
|
this.__scrollHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Event} ev
|
||||||
|
*/
|
||||||
|
__clickHandler(ev) {
|
||||||
|
const el = /** @type {HTMLElement} */ (ev.target);
|
||||||
|
if (el.classList.contains('anchor')) {
|
||||||
|
const anchor =
|
||||||
|
el instanceof HTMLAnchorElement
|
||||||
|
? el
|
||||||
|
: /** @type{HTMLAnchorElement} */ (el.querySelector('a.anchor'));
|
||||||
|
ev.preventDefault();
|
||||||
|
this.dispatchEvent(new Event('close-overlay', { bubbles: true }));
|
||||||
|
// wait for closing animation to finish before start scrolling
|
||||||
|
setTimeout(() => {
|
||||||
|
const parsedUrl = new URL(anchor.href);
|
||||||
|
document.location.hash = parsedUrl.hash;
|
||||||
|
}, 250);
|
||||||
|
}
|
||||||
|
if (!this.hasAttribute('no-redirects')) {
|
||||||
|
const links = el.parentElement?.querySelectorAll('ul a');
|
||||||
|
if (links && links.length > 1) {
|
||||||
|
const subLink = /** @type {HTMLAnchorElement} */ (links[1]);
|
||||||
|
if (!subLink.classList.contains('anchor')) {
|
||||||
|
ev.preventDefault();
|
||||||
|
subLink.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
__scrollHandler() {
|
__scrollHandler() {
|
||||||
for (const listObj of this.list) {
|
for (const listObj of this.list) {
|
||||||
listObj.top = listObj.headline.getBoundingClientRect().top;
|
listObj.top = listObj.headline.getBoundingClientRect().top;
|
||||||
|
|||||||
@@ -58,7 +58,8 @@ describe('rocket-navigation', () => {
|
|||||||
expect(anchorSpy).to.not.be.called;
|
expect(anchorSpy).to.not.be.called;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('will mark the currently "active" headline in the menu', async () => {
|
it('will mark the currently "active" headline in the menu', async function () {
|
||||||
|
this.timeout(5000);
|
||||||
function addBlock(headline, length = 5) {
|
function addBlock(headline, length = 5) {
|
||||||
return html`
|
return html`
|
||||||
<h2 id="${headline}">${headline}</h2>
|
<h2 id="${headline}">${headline}</h2>
|
||||||
@@ -96,20 +97,20 @@ describe('rocket-navigation', () => {
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`);
|
`);
|
||||||
await aTimeout(0);
|
await aTimeout(50);
|
||||||
const anchorLis = wrapper.querySelectorAll('.menu-item.anchor');
|
const anchorLis = wrapper.querySelectorAll('.menu-item.anchor');
|
||||||
expect(anchorLis[0]).to.have.class('current');
|
expect(anchorLis[0]).to.have.class('current');
|
||||||
expect(anchorLis[1]).to.not.have.class('current');
|
expect(anchorLis[1]).to.not.have.class('current');
|
||||||
expect(anchorLis[2]).to.not.have.class('current');
|
expect(anchorLis[2]).to.not.have.class('current');
|
||||||
|
|
||||||
document.querySelector('#middle').scrollIntoView();
|
document.querySelector('#middle').scrollIntoView();
|
||||||
await aTimeout(20);
|
await aTimeout(100);
|
||||||
expect(anchorLis[0]).to.not.have.class('current');
|
expect(anchorLis[0]).to.not.have.class('current');
|
||||||
expect(anchorLis[1]).to.have.class('current');
|
expect(anchorLis[1]).to.have.class('current');
|
||||||
expect(anchorLis[2]).to.not.have.class('current');
|
expect(anchorLis[2]).to.not.have.class('current');
|
||||||
|
|
||||||
document.querySelector('#bottom').scrollIntoView();
|
document.querySelector('#bottom').scrollIntoView();
|
||||||
await aTimeout(20);
|
await aTimeout(100);
|
||||||
expect(anchorLis[0]).to.not.have.class('current');
|
expect(anchorLis[0]).to.not.have.class('current');
|
||||||
expect(anchorLis[1]).to.not.have.class('current');
|
expect(anchorLis[1]).to.not.have.class('current');
|
||||||
expect(anchorLis[2]).to.have.class('current');
|
expect(anchorLis[2]).to.have.class('current');
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user