feat: initial Astro integration

Co-authored-by: Oleksii Kadurin <ovkadurin@gmail.com>; Thijs Louisse <Thijs.Louisse@ing.com>
This commit is contained in:
okadurin 2023-10-17 09:41:35 +02:00 committed by Thijs Louisse
parent 4c077fbf1d
commit 70b0241189
83 changed files with 16385 additions and 17774 deletions

View file

@ -4,9 +4,6 @@ bundlesize/
.history/
storybook-static/
*.d.ts
_site-dev
_site
docs/_merged_*
patches/
/docs/_assets/scoped-custom-element-registry.min.js

22
.gitignore vendored
View file

@ -1,6 +1,15 @@
# build output
dist/
# generated types
.astro/
.astro
# generated __mdjs-stories.js files
public/docs
## editors
/.idea
## disable this line one commit before changing contents that should be shared
/.vscode
/*.code-workspace
/.history
@ -44,17 +53,8 @@ local.log
## browserstack
browserstack.err
## Rocket ignore files (need to be the full relative path to the folders)
docs/_merged_data/
docs/_merged_assets/
docs/_merged_includes/
docs/**/api-table.md
debug.log
_site
_site-dev
## generated test fiels
__output
.wireit
@ -63,4 +63,4 @@ __output
packages/ui/new-exports/
packages/ui/test-exports/
src/content/docs

View file

@ -11,5 +11,8 @@
"no-trailing-punctuation": {
"punctuation": ".,;。,;:!"
},
"ol-prefix": false
"ol-prefix": false,
"MD025": {
"front_matter_title": ""
}
}

View file

@ -1,8 +1,6 @@
coverage/
CHANGELOG.md
bundlesize/
_site
_site-dev
.history
/docs/_assets/scoped-custom-element-registry.min.js

View file

@ -26,33 +26,59 @@
</p>
<p align="center">
<a href="https://lion.js.org">Website</a>
<a href="https://lion-web.netlify.app">Website</a>
·
<a href="https://lion.js.org/fundamentals/">Fundamentals</a>
<a href="https://lion-web.netlify.app/fundamentals/">Fundamentals</a>
·
<a href="https://lion.js.org/guides/">Guides</a>
<a href="https://lion-web.netlify.app/guides/">Guides</a>
·
<a href="https://lion.js.org/components/">Components</a>
<a href="https://lion-web.netlify.app/components/">Components</a>
·
<a href="https://lion.js.org/blog/">Blog</a>
<a href="https://lion-web.netlify.app/blog/">Blog</a>
</p>
**Lion is a set of highly performant, accessible, and flexible Web Components.**
**Lion is a set of highly performant, accessible and flexible Web Components.!**
They provide an unopinionated, white-label layer that can be extended to your own layer of components.
- **High Performance:** Focused on great performance in all relevant browsers with a minimal number of dependencies.
- **Accessibility:** Aimed at compliance with the WCAG 2.2 AA standard to create components that are accessible for everybody.
- **Flexibility:** Provides solutions through Web Components and JavaScript classes which can be used, adopted and extended to fit all needs.
- **Modern Code:** Lion is distributed as pure ES modules.
- **Exposes functions/classes and Web Components:** Ships a functionality in its most appropriate form.
- **Modern Code:** Lion is distributes as pure es modules.
- **Exposes functions/classes and Web Components:** Ships a functionality in it's most appropriate form.
> Note: Our demo examples might look simple and not very stylish. This is on purpose. They are designed to be basic so you can easily add your own styles to them to match your intended design, without dealing with styles that are already there.
> Note: Our demos may look a little bland but that is on purpose. They only come with functional stylings.
> This makes sense as the main use case is to extend those components and if you do you do not want to override existing stylings.
<p align="center">
<a href="https://lion.js.org/guides/"><strong>Explore the Lion Guides&nbsp;&nbsp;</strong></a>
<a href="https://lion-web.netlify.app/guides/"><strong>Explore the Lion Guides&nbsp;&nbsp;</strong></a>
</p>
## Astro migration
- Keep using `/docs` on the root level as we used it in the `master` branch. The documentation is copied into Astro related directories on `npm run start` and when when anything in `/docs` is updated.
- Replace manually all references to assets in all `md` files so that we imply that the path is produced from the directory where the md file is located. F.e. `new URL('../src/wa-combobox/assets/obama.jpeg', import.meta.url).href;` should `new URL('./src/wa-combobox/assets/obama.jpeg', import.meta.url).href;`. Note double dot is replaced with one dot. See [this PR](https://github.com/ing-bank/lion/pull/2125/files#diff-403b1e0ab54d51dcfe54248e847498e492da00383d8b33a4087994ab9035a22c) for the reference.
- Rename all `*.mjs` files to `*.js` ones if they are used by `mdjs` examples
### Astro how to
- To start in dev mode run `nmm run start`
- To start in production mode:
- Run `npm run build`
- Run `npm run preview`
### Issues which are not caused by the migration (not to be fixed now)
- There is a browser console error on [collapsible page](http://localhost:4321/components/collapsible):
```
__mdjs-stories--use-cases.js:40 Uncaught TypeError: shadowRoot.getElementById is not a function
```
Note. There is the same error on master. This issue is not caused by the migration
### TODO
## How to install
```bash
@ -113,7 +139,7 @@ You can also use the lion elements directly, although this is likely not a commo
## Issues
If you encounter an issue with any of the packages we are offering please open a [new bug issue](https://github.com/ing-bank/lion/issues/new?assignees=&labels=&template=bug_report.md&title=). Be sure to include a description of the expected and the current behavior - additionally adding a [reproduction](https://webcomponents.dev/edit/kpZmz1CJN580OaXsk56f?pm=1) always helps.
If you encounter an issue with any of the packages we are offering please open a [new bug issue](https://github.com/ing-bank/lion/issues/new?assignees=&labels=&template=bug_report.md&title=). Be sure to include a description of the expected and the current behavior - additional adding a [reproduction](https://webcomponents.dev/edit/kpZmz1CJN580OaXsk56f?pm=1) always helps.
## Feature requests
@ -158,7 +184,7 @@ This means you only have to apply your own Design System: by delivering styles,
## Coding guidelines
Check out our [coding guidelines](https://lion.js.org/guides/principles/definitions-and-terms/) for more detailed information.
Check out our [coding guidelines](https://lion-web.netlify.app/guides/principles/definitions-and-terms/) for more detailed information.
## How to contribute

53
astro.config.mjs Normal file
View file

@ -0,0 +1,53 @@
import { defineConfig } from "astro/config";
import lit from "@astrojs/lit";
import { mdjsParse, mdjsStoryParse, mdjsSetupCode } from '@mdjs/core';
import { copyMdjsStories } from './src/utils/remark-plugings/copyMdjsStories/index.js';
import { cleanupRocketMetadata } from './src/utils/remark-plugings/cleanupRocketMetadata/index.js';
import { updateMainTagsForMdjsStories } from './src/utils/remark-plugings/updateMainTagsForMdjsStories/index.js';
const mdjsSetupConfig = {
simulationSettings: {
simulatorUrl: '/simulator/',
languages: [
{ key: 'de-DE', name: 'German' },
{ key: 'en-GB', name: 'English (United Kingdom)' },
{ key: 'en-US', name: 'English (United States)' },
{ key: 'nl-NL', name: 'Dutch' },
],
},
};
// https://astro.build/config
export default defineConfig({
integrations: [lit()],
markdown: {
remarkPlugins: [updateMainTagsForMdjsStories, mdjsParse, mdjsStoryParse, [mdjsSetupCode, mdjsSetupConfig], copyMdjsStories, cleanupRocketMetadata],
},
vite: {
// the fix is copied from https://github.com/withastro/astro/issues/5517#issuecomment-1337328843.
// This allows to import rocket-preset-extend-lion-docs. The following error pops up otherwise:
// ```
// [ERROR] Top-level await is not available in the configured target environment ("chrome87", "edge88", "es2020", "firefox78", "safari14" + 2 overrides)
// node_modules/rocket-preset-extend-lion-docs/src/getPublicApiOfPkg.js:6:0:
// 6 │ await init;
// ```
optimizeDeps: {
exclude: ['rocket-preset-extend-lion-docs']
},
// Fix taken from https://github.com/vitejs/vite/issues/6985#issuecomment-1044375490.
// It throws an error otherwise:
// ```
// astro-poc2/node_modules/vite/dist/node/chunks/dep-df561101.js:43799
// const err = new Error('The server is being restarted or closed. Request is outdated');
// ```
// Note, if this erorr is still present, as a workaround try adding 'esnext' to node_modules/vite/dist/node/constants.js -> ESBUILD_MODULES_TARGET
// build: {
// target: 'esnext'
// },
build: {
rollupOptions: {
external: /^lit/
}
}
},
});

10
config.mjs Normal file
View file

@ -0,0 +1,10 @@
export const maxDepthForNonComponentsNavigation = 2;
export const docsDirName = '/docs/';
export const isToBeConcatenated = (path) => {
if (path.includes('/components/')) {
return true;
}
const pathAfterDocs = path.split(docsDirName)[1];
const numberOfSections = pathAfterDocs.split('/').length;
return numberOfSections > maxDepthForNonComponentsNavigation;
}

View file

@ -1,20 +1,8 @@
---
parts:
- Button
- Extensions
title: 'Button: Extensions'
eleventyNavigation:
key: 'Button: Extensions'
order: 90
parent: Button
title: Extensions
---
# Button: Extensions
# Button >> Extensions ||90
```js script
import { html } from '@mdjs/mdjs-preview';
import './extensions/bootstrap-button.mjs';
import './extensions/bootstrap-button.js';
```
## Bootstrap button

View file

@ -1,13 +1,3 @@
---
parts:
- Button
title: Button
eleventyNavigation:
key: Button
order: 10
title: Button
---
# Button
# Button ||10
-> go to Overview

View file

@ -1,16 +1,4 @@
---
parts:
- Button
- Overview
title: 'Button: Overview'
eleventyNavigation:
key: 'Button: Overview'
order: 10
parent: Button
title: Overview
---
# Button: Overview
# Button >> Overview ||10
A button web component that is easily stylable and accessible.

View file

@ -1,16 +1,4 @@
---
parts:
- Button
- Use Cases
title: 'Button: Use Cases'
eleventyNavigation:
key: 'Button: Use Cases'
order: 20
parent: Button
title: Use Cases
---
# Button: Use Cases
# Button >> Use Cases ||20
```js script
import { html } from '@mdjs/mdjs-preview';
@ -40,8 +28,7 @@ export const disabled = () => html`<lion-button disabled>Disabled</lion-button>`
The minimum click area needs to be at least `44px` by `44px` according to [WCAG Success Criterion 2.5.5 Target Size (Enhanced)](https://www.w3.org/TR/WCAG22/#target-size-enhanced).
```js preview-story
export const minimumClickArea = () =>
html` <style>
export const minimumClickArea = () => html` <style>
.small {
padding: 4px;
line-height: 1em;
@ -74,10 +61,10 @@ export const withinForm = () => html`
<input id="firstNameId" name="firstName" />
<label for="lastNameId">Last name</label>
<input id="lastNameId" name="lastName" />
<lion-button-submit @click="${ev => console.log('click submit handler', ev.target)}"
<lion-button-submit @click=${ev => console.log('click submit handler', ev.target)}
>Submit</lion-button-submit
>
<lion-button-reset @click="${ev => console.log('click reset handler', ev.target)}"
<lion-button-reset @click=${ev => console.log('click reset handler', ev.target)}
>Reset</lion-button-reset
>
</form>

View file

@ -59,11 +59,11 @@ export const Github = () => html`
```js preview-story
export const Whatsapp = () => {
const obamaImgUrl = new URL('../src/wa-combobox/assets/obama.jpeg', import.meta.url).href;
const trumpImgUrl = new URL('../src/wa-combobox/assets/trump.jpeg', import.meta.url).href;
const bidenImgUrl = new URL('../src/wa-combobox/assets/biden.jpeg', import.meta.url).href;
const bushImgUrl = new URL('../src/wa-combobox/assets/bush.jpeg', import.meta.url).href;
const clintonImgUrl = new URL('../src/wa-combobox/assets/clinton.jpeg', import.meta.url).href;
const obamaImgUrl = new URL('./src/wa-combobox/assets/obama.jpeg', import.meta.url).href;
const trumpImgUrl = new URL('./src/wa-combobox/assets/trump.jpeg', import.meta.url).href;
const bidenImgUrl = new URL('./src/wa-combobox/assets/biden.jpeg', import.meta.url).href;
const bushImgUrl = new URL('./src/wa-combobox/assets/bush.jpeg', import.meta.url).href;
const clintonImgUrl = new URL('./src/wa-combobox/assets/clinton.jpeg', import.meta.url).href;
return html`
<wa-combobox name="combo" label="Filter chats">

View file

@ -20,19 +20,6 @@ import './assets/iconset-misc.js';
import * as spaceSet from './assets/iconset-space.js';
import '@lion/ui/define/lion-icon.js';
icons.addIconResolver('lion', (iconset, name) => {
switch (iconset) {
case 'bugs':
return import('./assets/iconset-bugs.js').then(module => module[name]);
case 'space':
return import('./assets/iconset-space.js').then(module => module[name]);
case 'misc':
return import('./assets/iconset-misc.js').then(module => module[name]);
default:
throw new Error(`Unknown iconset ${iconset}`);
}
});
```
## Icon sets

View file

@ -3,7 +3,7 @@
import { html, LitElement, css } from 'lit';
import { ref, createRef } from 'lit/directives/ref.js';
import { OverlayMixin } from '@lion/ui/overlays.js';
import './demo-el-using-overlaymixin.mjs';
import './demo-el-using-overlaymixin.js';
/**
* @typedef {import('@lion/ui/types/overlays.js').OverlayConfig} OverlayConfig

View file

@ -1,23 +1,10 @@
---
parts:
- Configuration
- Overlays
- Systems
title: 'Overlays: Configuration'
eleventyNavigation:
key: Systems >> Overlays >> Configuration
title: Configuration
order: 40
parent: Systems >> Overlays
---
# Systems >> Overlays >> Configuration ||40
```js script
import { html } from '@mdjs/mdjs-preview';
import './assets/demo-el-using-overlaymixin.mjs';
import './assets/applyDemoOverlayStyles.mjs';
import { withDropdownConfig, withModalDialogConfig, withTooltipConfig } from '@lion/ui/overlays.js';
import './assets/demo-el-using-overlaymixin.js';
import './assets/applyDemoOverlayStyles.js';
import { withDropdownConfig, withTooltipConfig } from '@lion/ui/overlays.js';
```
The `OverlayController` has many configuration options.
@ -42,13 +29,13 @@ export const placementLocal = () => {
border: 1px solid black;
}
</style>
<demo-el-using-overlaymixin .config="${placementModeLocalConfig}">
<demo-el-using-overlaymixin .config=${placementModeLocalConfig}>
<button slot="invoker">Click me to open the local overlay!</button>
<div slot="content" class="demo-overlay">
Hello! You can close this notification here:
<button
class="close-button"
@click="${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}"
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
>
</button>
@ -64,13 +51,13 @@ export const placementLocal = () => {
export const placementGlobal = () => {
const placementModeGlobalConfig = { placementMode: 'global' };
return html`
<demo-el-using-overlaymixin .config="${placementModeGlobalConfig}">
<demo-el-using-overlaymixin .config=${placementModeGlobalConfig}>
<button slot="invoker">Click me to open the global overlay!</button>
<div slot="content" class="demo-overlay">
Hello! You can close this notification here:
<button
class="close-button"
@click="${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}"
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
>
</button>
@ -80,36 +67,6 @@ export const placementGlobal = () => {
};
```
## isAlertDialog
In some cases the dialog should act like an [alertdialog](https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/examples/alertdialog/), which is a combination of an alert and dialog. If that is the case, you can add `is-alert-dialog` attribute, which sets the correct role on the dialog.
```js preview-story
export const alertDialog = () => {
const placementModeGlobalConfig = { ...withModalDialogConfig(), isAlertDialog: true };
return html`
<demo-el-using-overlaymixin .config="${placementModeGlobalConfig}">
<button slot="invoker">Click me to open the alert dialog!</button>
<div slot="content" class="demo-overlay">
Are you sure you want to perform this action?
<button
type="button"
@click="${ev => ev.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}"
>
Yes
</button>
<button
type="button"
@click="${ev => ev.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}"
>
No
</button>
</div>
</demo-el-using-overlaymixin>
`;
};
```
## isTooltip (placementMode: 'local')
As specified in the [overlay rationale](./rationale.md) there are only two official types of overlays: dialogs and tooltips. And their main differences are:
@ -125,7 +82,7 @@ export const usingTooltipConfig = () => {
const tooltipConfig = { ...withTooltipConfig() };
return html`
<demo-el-using-overlaymixin id="tooltip" .config="${tooltipConfig}">
<demo-el-using-overlaymixin id="tooltip" .config=${tooltipConfig}>
<button slot="invoker">Hover me to open the tooltip!</button>
<div slot="content" class="demo-overlay">Hello!</div>
</demo-el-using-overlaymixin>
@ -146,7 +103,7 @@ You use the feature on any type of overlay.
export const trapsKeyboardFocus = () => {
const trapsKeyboardFocusConfig = { ...withDropdownConfig(), trapsKeyboardFocus: true };
return html`
<demo-el-using-overlaymixin .config="${trapsKeyboardFocusConfig}">
<demo-el-using-overlaymixin .config=${trapsKeyboardFocusConfig}>
<button slot="invoker">Click me to open the overlay!</button>
<div slot="content" class="demo-overlay">
<div><a href="#">A focusable anchor</a></div>
@ -154,7 +111,7 @@ export const trapsKeyboardFocus = () => {
Hello! You can close this notification here:
<button
class="close-button"
@click="${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}"
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
>
</button>
@ -172,39 +129,13 @@ Boolean property. Will allow closing the overlay on ESC key when enabled.
export const hidesOnEsc = () => {
const hidesOnEscConfig = { ...withDropdownConfig(), hidesOnEsc: true };
return html`
<demo-el-using-overlaymixin .config="${hidesOnEscConfig}">
<demo-el-using-overlaymixin .config=${hidesOnEscConfig}>
<button slot="invoker">Click me to open the overlay!</button>
<div slot="content" class="demo-overlay">
Hello! You can close this notification here:
<button
class="close-button"
@click="${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}"
>
</button>
</div>
</demo-el-using-overlaymixin>
`;
};
```
And how it works if `hidesOnEsc` is disabled. In most cases `hidesOnOutsideEsc` needs also to be set to `false`.
```js preview-story
export const hidesOnEscFalse = () => {
const hidesOnEscConfig = {
...withModalDialogConfig(),
hidesOnEsc: false,
hidesOnOutsideEsc: false,
};
return html`
<demo-el-using-overlaymixin .config="${hidesOnEscConfig}">
<button slot="invoker">Click me to open the overlay!</button>
<div slot="content" class="demo-overlay">
Hello! You can close this notification here:
<button
class="close-button"
@click="${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}"
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
>
</button>
@ -222,13 +153,13 @@ Boolean property. When enabled allows closing the overlay on ESC key, even when
export const hidesOnOutsideEsc = () => {
const hidesOnEscConfig = { ...withDropdownConfig(), hidesOnOutsideEsc: true };
return html`
<demo-el-using-overlaymixin .config="${hidesOnEscConfig}">
<demo-el-using-overlaymixin .config=${hidesOnEscConfig}>
<button slot="invoker">Click me to open the overlay!</button>
<div slot="content" class="demo-overlay">
Hello! You can close this notification here:
<button
class="close-button"
@click="${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}"
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
>
</button>
@ -246,14 +177,14 @@ Boolean property. Will allow closing the overlay by clicking outside the `conten
export const hidesOnOutsideClick = () => {
const hidesOnOutsideClickConfig = { ...withDropdownConfig(), hidesOnOutsideClick: true };
return html`
<demo-el-using-overlaymixin .config="${hidesOnOutsideClickConfig}">
<demo-el-using-overlaymixin .config=${hidesOnOutsideClickConfig}>
<button slot="invoker">Click me to open the overlay!</button>
<div slot="content" class="demo-overlay">
<label for="myInput">Clicking this label should not trigger close</label>
<input id="myInput" />
<button
class="close-button"
@click="${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}"
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
>
</button>
@ -276,13 +207,13 @@ export const elementToFocusAfterHide = () => {
const elementToFocusAfterHideConfig = { ...withDropdownConfig(), elementToFocusAfterHide: btn };
return html`
<demo-el-using-overlaymixin .config="${elementToFocusAfterHideConfig}">
<demo-el-using-overlaymixin .config=${elementToFocusAfterHideConfig}>
<button slot="invoker">Click me to open the overlay!</button>
<div slot="content" class="demo-overlay">
Hello! You can close this notification here:
<button
class="close-button"
@click="${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}"
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
>
</button>
@ -312,13 +243,13 @@ This currently only supports CSS Animations, because it relies on the `animation
export const hasBackdrop = () => {
const hasBackdropConfig = { ...withDropdownConfig(), hasBackdrop: true };
return html`
<demo-el-using-overlaymixin .config="${hasBackdropConfig}">
<demo-el-using-overlaymixin .config=${hasBackdropConfig}>
<button slot="invoker">Click me to open the overlay!</button>
<div slot="content" class="demo-overlay">
Hello! You can close this notification here:
<button
class="close-button"
@click="${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}"
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
>
</button>
@ -342,19 +273,19 @@ export const isBlocking = () => {
This overlay gets closed when overlay B gets opened
<button
class="close-button"
@click="${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}"
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
>
</button>
</div>
</demo-el-using-overlaymixin>
<demo-el-using-overlaymixin .config="${isBlockingConfig}">
<demo-el-using-overlaymixin .config=${isBlockingConfig}>
<button slot="invoker">Overlay B: open second</button>
<div slot="content" class="demo-overlay demo-overlay--blocking">
Overlay A is hidden... now close me and see overlay A again.
<button
class="close-button"
@click="${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}"
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
>
</button>
@ -375,13 +306,13 @@ Boolean property. When true, prevents scrolling content that is outside of the `
export const preventsScroll = () => {
const preventsScrollConfig = { preventsScroll: true };
return html`
<demo-el-using-overlaymixin .config="${preventsScrollConfig}">
<demo-el-using-overlaymixin .config=${preventsScrollConfig}>
<button slot="invoker">Click me to open the overlay!</button>
<div slot="content" class="demo-overlay">
Hello! You can close this notification here:
<button
class="close-button"
@click="${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}"
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
>
</button>
@ -414,13 +345,13 @@ export const viewportConfig = () => {
viewportConfig: { placement: 'bottom-left' },
};
return html`
<demo-el-using-overlaymixin .config="${viewportConfig}">
<demo-el-using-overlaymixin .config=${viewportConfig}>
<button slot="invoker">Click me to open the overlay in the bottom left corner!</button>
<div slot="content" class="demo-overlay">
Hello! You can close this notification here:
<button
class="close-button"
@click="${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}"
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
>
</button>
@ -494,13 +425,13 @@ export const popperConfig = () => {
border: 1px solid black;
}
</style>
<demo-el-using-overlaymixin .config="${popperConfig}">
<demo-el-using-overlaymixin .config=${popperConfig}>
<button slot="invoker">Click me to open the overlay!</button>
<div slot="content" class="demo-overlay">
Hello! You can close this notification here:
<button
class="close-button"
@click="${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}"
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
>
</button>

View file

@ -24,9 +24,9 @@ import {
withModalDialogConfig,
} from '@lion/ui/overlays.js';
import './assets/demo-el-using-overlaymixin.mjs';
import './assets/demo-overlay-backdrop.mjs';
import './assets/applyDemoOverlayStyles.mjs';
import './assets/demo-el-using-overlaymixin.js';
import './assets/demo-overlay-backdrop.js';
import './assets/applyDemoOverlayStyles.js';
import { ref, createRef } from 'lit/directives/ref.js';
```

View file

@ -201,7 +201,7 @@ const cacheOptions = {
maxAge: TEN_MINUTES,
};
const [cacheRequestInterceptor, cacheResponseInterceptor] = createCacheInterceptors(
const { cacheRequestInterceptor, cacheResponseInterceptor } = createCacheInterceptors(
getCacheIdentifier,
cacheOptions,
);

View file

@ -35,7 +35,7 @@ const cacheOptions = {
maxAge: TEN_MINUTES,
};
const [cacheRequestInterceptor, cacheResponseInterceptor] = createCacheInterceptors(
const { cacheRequestInterceptor, cacheResponseInterceptor } = createCacheInterceptors(
getCacheIdentifier,
cacheOptions,
);
@ -204,7 +204,7 @@ const globalCacheOptions = {
// for instance when a current user is logged out
const getCacheIdentifier = () => getActiveProfile().profileId;
const [cacheRequestInterceptor, cacheResponseInterceptor] = createCacheInterceptors(
const { cacheRequestInterceptor, cacheResponseInterceptor } = createCacheInterceptors(
getCacheIdentifier,
globalCacheOptions,
);

View file

@ -1,15 +1,3 @@
---
parts:
- Helpers
- Tools
title: 'Tools: Helpers'
eleventyNavigation:
key: Tools >> Helpers
title: Helpers
order: 0
parent: Tools
---
# Tools >> Helpers
# Tools >> Helpers ||5
-> go to Overview

View file

@ -1,7 +1,7 @@
import { LitElement, css, html } from 'lit';
import 'page-a/page-a.js';
import 'page-b/page-b.js';
import 'page-c/page-c.js';
import 'page-d/page-d.js';
class DemoApp extends LitElement {
constructor() {
@ -85,9 +85,9 @@ class DemoApp extends LitElement {
Page B
</button>
</nav>
${this.page === 'A' ? html` <page-a></page-a> ` : html` <page-b></page-b> `}
${this.page === 'A' ? html` <page-c></page-c> ` : html` <page-d></page-d> `}
`;
}
}
customElements.define('demo-app', DemoApp);
customElements.define('demo-app-fail', DemoApp);

View file

@ -23,7 +23,7 @@ In this SPA (Single Page Application) demo you will be able to reproduce the iss
In an real application this would now mean that your users can no longer interact with your application.
<demo-app>Loading App...</demo-app>
<demo-app-fail>Loading App...</demo-app-fail>
<div id="overlay-target" style="margin-top: 50px;"></div>

View file

@ -1,5 +1,5 @@
{
"name": "page-a",
"name": "page-c",
"version": "1.0.0",
"dependencies": {
"overlays": "^1.0.0"

View file

@ -1,7 +1,7 @@
import { LitElement, html, css } from 'lit-element';
import { overlays } from 'overlays/instance.js';
export class PageA extends LitElement {
export class PageC extends LitElement {
static get styles() {
return css`
:host {
@ -31,4 +31,4 @@ export class PageA extends LitElement {
}
}
customElements.define('page-a', PageA);
customElements.define('page-c', PageC);

View file

@ -1,5 +1,5 @@
{
"name": "page-b",
"name": "page-d",
"version": "1.0.0",
"dependencies": {
"overlays": "^2.0.0"

View file

@ -1,7 +1,7 @@
import { LitElement, html, css } from 'lit-element';
import { overlays } from 'overlays/instance.js';
export class PageB extends LitElement {
export class PageD extends LitElement {
static get styles() {
return css`
:host {
@ -31,4 +31,4 @@ export class PageB extends LitElement {
}
}
customElements.define('page-b', PageB);
customElements.define('page-d', PageD);

View file

@ -2,8 +2,8 @@ import { LitElement, css, html } from 'lit';
import './overlayCompatibility.js';
import 'page-a/page-a.js';
import 'page-b/page-b.js';
import 'page-e/page-e.js';
import 'page-f/page-f.js';
class DemoApp extends LitElement {
constructor() {
@ -87,9 +87,9 @@ class DemoApp extends LitElement {
Page B
</button>
</nav>
${this.page === 'A' ? html` <page-a></page-a> ` : html` <page-b></page-b> `}
${this.page === 'A' ? html` <page-e></page-e> ` : html` <page-f></page-f> `}
`;
}
}
customElements.define('demo-app', DemoApp);
customElements.define('demo-app-success', DemoApp);

View file

@ -22,7 +22,7 @@ In this SPA (Single Page Application) demo you will be able to reproduce the sol
With this solutions users can not break the app anymore.
<demo-app>Loading App...</demo-app>
<demo-app-success>Loading App...</demo-app-success>
<div id="overlay-target" style="margin-top: 50px;"></div>

View file

@ -1,5 +1,5 @@
{
"name": "page-a",
"name": "page-e",
"version": "1.0.0",
"dependencies": {
"overlays": "^1.0.0"

View file

@ -1,7 +1,7 @@
import { LitElement, html, css } from 'lit-element';
import { overlays } from 'overlays/instance.js';
export class PageA extends LitElement {
export class PageE extends LitElement {
static get styles() {
return css`
:host {
@ -30,5 +30,4 @@ export class PageA extends LitElement {
`;
}
}
customElements.define('page-a', PageA);
customElements.define('page-e', PageE);

View file

@ -1,5 +1,5 @@
{
"name": "page-b",
"name": "page-f",
"version": "1.0.0",
"dependencies": {
"overlays": "^2.0.0"

View file

@ -1,7 +1,7 @@
import { LitElement, html, css } from 'lit-element';
import { overlays } from 'overlays/instance.js';
export class PageB extends LitElement {
export class PageF extends LitElement {
static get styles() {
return css`
:host {
@ -31,4 +31,4 @@ export class PageB extends LitElement {
}
}
customElements.define('page-b', PageB);
customElements.define('page-f', PageF);

View file

@ -1,15 +1,3 @@
---
parts:
- Singleton Manager
- Tools
title: 'Tools: Singleton Manager'
eleventyNavigation:
key: Tools >> Singleton Manager
title: Singleton Manager
order: 20
parent: Tools
---
# Tools >> Singleton Manager ||20
# Tools >> Singleton Manager ||30
-> go to Overview

31671
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -3,14 +3,18 @@
"name": "@lion/root",
"license": "MIT",
"scripts": {
"build": "rocket build",
"build": "export PROD=true && npm run build-docs && npx astro build && node -e 'require(\"./src/utils/post-build-dist/post-build-dist.js\").postBuildDist()'",
"build-docs": "npm run clean && node -e 'require(\"./src/utils/copy-docs\").copyDocs()' && node -e 'require(\"./src/utils/copy-docs\").processImports()'",
"build-docs:watch": "node -e 'require(\"./src/utils/copy-docs\").watch()'",
"bundlesize": "rollup -c bundlesize/rollup.config.js && bundlesize",
"changeset": "changeset",
"custom-elements-manifest": "npm run custom-elements-manifest --workspaces --if-present && node ./scripts/create-api-tables.mjs",
"clean": "rm -rf ./public/docs && rm -rf ./src/content/docs && rm -rf ./dist",
"clean:full": "npm run clean && rm -rf ./node_modules",
"custom-elements-manifest": "npm run custom-elements-manifest --workspaces --if-present",
"debug": "web-test-runner --watch --config web-test-runner-chrome.config.mjs",
"debug:firefox": "web-test-runner --watch --config web-test-runner-firefox.config.mjs",
"debug:no-scoped-registries-polyfill": "npm run debug -- --no-scoped-registries-polyfill",
"debug:webkit": "web-test-runner --watch --config web-test-runner-webkit.config.mjs",
"dev": "astro dev",
"format": "npm run format:eslint && npm run format:prettier",
"format:eslint": "eslint --ext .js,.html . --fix",
"format:prettier": "prettier \"**/*.{js,md}\" \"packages/*/package.json\" \"package.json\" --write",
@ -22,79 +26,101 @@
"lint:types": "npm run types",
"lint:versions": "node ./scripts/lint-versions.js",
"prepare": "husky install",
"preview": "astro preview",
"release": "changeset publish",
"repo:clean-slate": "git clean -dfX",
"repo:diff-package-lock": "npx diff-package-lock",
"start": "rocket start",
"rm-all-node_modules": "npm exec --workspaces -- npx rimraf node_modules && npx rimraf node_modules",
"start": "npm run build-docs && npm run watch & astro dev",
"start:prod": "npm run preview",
"test": "run-p test:browser test:node",
"test:browser": "web-test-runner --coverage",
"test:browserstack": "web-test-runner --config ./web-test-runner-browserstack.config.js",
"test:node": "npm run test:node --workspaces --if-present",
"test:screenshots": "npx rimraf screenshots/.diff/ && npx rimraf screenshots/.current/ && mocha --require scripts/screenshots/bootstrap.js --exit --timeout 10000 \"packages/**/test/*.screenshots-test.js\"",
"test:screenshots": "rimraf screenshots/.diff/ && rimraf screenshots/.current/ && mocha --require scripts/screenshots/bootstrap.js --exit --timeout 10000 \"packages/**/test/*.screenshots-test.js\"",
"test:screenshots:update": "cross-env UPDATE_SCREENSHOTS=true npm run test:screenshots",
"types": "npm run types --workspaces --if-present",
"types-check-only": "npm run types-check-only --workspaces --if-present"
"types-check-only": "npm run types-check-only --workspaces --if-present",
"watch": "npm-watch"
},
"workspaces": [
"packages/*",
"packages-node/*"
],
"dependencies": {
"@astrojs/lit": "^3.0.2",
"@mdjs/core": "^0.20.0",
"@rollup/plugin-commonjs": "^24.0.0",
"@rollup/plugin-node-resolve": "^13.0.6",
"astro": "^3.3.1",
"astro-seo": "^0.8.0",
"changeset": "^0.2.6",
"find-node-modules": "^2.1.3",
"image-extensions": "^1.1.0",
"npm-watch": "^0.11.0"
},
"devDependencies": {
"@babel/core": "^7.25.8",
"@babel/core": "^7.10.1",
"@bundled-es-modules/fetch-mock": "^6.5.2",
"@changesets/cli": "^2.27.9",
"@custom-elements-manifest/analyzer": "^0.10.3",
"@custom-elements-manifest/to-markdown": "^0.1.0",
"@lit-labs/testing": "^0.2.5",
"@lit/reactive-element": "^2.1.0",
"@open-wc/building-rollup": "^2.2.3",
"@open-wc/eslint-config": "^12.0.3",
"@open-wc/scoped-elements": "^3.0.5",
"@open-wc/testing": "^4.0.0",
"@open-wc/testing-helpers": "^3.0.1",
"@rocket/blog": "0.4.0",
"@rocket/cli": "^0.10.2",
"@rocket/launch": "^0.6.0",
"@rocket/search": "^0.5.1",
"@thepassle/module-graph": "^0.11.0",
"@types/autosize": "^4.0.3",
"@types/chai-as-promised": "^8.0.1",
"@types/chai-dom": "^1.11.3",
"@types/fs-extra": "^11.0.4",
"@types/glob": "^8.1.0",
"@types/mocha": "^10.0.9",
"@web/dev-server-legacy": "^2.1.1",
"@web/test-runner": "^0.19.0",
"@web/test-runner-browserstack": "^0.7.2",
"@web/test-runner-commands": "^0.9.0",
"@web/test-runner-playwright": "^0.11.0",
"@webcomponents/scoped-custom-element-registry": "^0.0.10",
"@changesets/cli": "^2.26.1",
"@custom-elements-manifest/analyzer": "^0.5.7",
"@open-wc/building-rollup": "^1.2.1",
"@open-wc/eslint-config": "^7.0.0",
"@open-wc/testing": "^3.1.2",
"@open-wc/testing-helpers": "^2.1.2",
"@types/chai-as-promised": "^7.1.5",
"@types/chai-dom": "^0.0.8",
"@types/convert-source-map": "^1.5.1",
"@types/fs-extra": "^9.0.7",
"@types/glob": "^7.1.3",
"@types/istanbul-reports": "^3.0.0",
"@types/mocha": "^10.0.0",
"@types/prettier": "^2.2.1",
"@web/dev-server": "^0.1.8",
"@web/dev-server-legacy": "^0.1.7",
"@web/test-runner": "^0.13.7",
"@web/test-runner-browserstack": "^0.5.0",
"@web/test-runner-commands": "^0.6.1",
"@web/test-runner-playwright": "^0.8.8",
"@webcomponents/scoped-custom-element-registry": "^0.0.5",
"@yarnpkg/lockfile": "^1.1.0",
"babel-polyfill": "^6.26.0",
"bundlesize": "^1.0.0-beta.2",
"cem-plugin-vs-code-custom-data-generator": "^1.4.2",
"chai": "^4.5.0",
"chai-as-promised": "^8.0.0",
"cross-env": "^7.0.3",
"eslint": "^8.57.1",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-wc": "^2.2.0",
"globby": "^14.0.2",
"gray-matter": "^4.0.3",
"husky": "^9.1.6",
"lint-staged": "^15.2.10",
"looks-same": "^9.0.1",
"markdownlint-cli": "^0.42.0",
"mocha": "^10.7.3",
"cem-plugin-vs-code-custom-data-generator": "^1.4.1",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"chalk": "^4.1.0",
"concurrently": "^5.2.0",
"cross-env": "^7.0.2",
"es6-promisify": "^6.1.1",
"eslint": "^8.26.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-lit": "^1.6.1",
"eslint-plugin-lit-a11y": "^2.2.0",
"eslint-plugin-wc": "^1.3.2",
"globby": "^13.1.2",
"husky": "^6.0.0",
"lint-staged": "^10.0.0",
"looks-same": "^7.2.3",
"markdownlint-cli": "^0.17.0",
"mermaid": "^9.3.0",
"mkdirp-promise": "^5.0.1",
"mocha": "^10.1.0",
"mock-fs": "^5.1.2",
"npm-run-all": "^4.1.5",
"patch-package": "^8.0.0",
"nyc": "^15.0.0",
"playwright": "^1.20.0",
"postinstall-postinstall": "^2.1.0",
"prettier": "^3.3.3",
"prettier-package-json": "^2.8.0",
"remark-html": "^13.0.2",
"semver": "^7.6.3",
"sinon": "^19.0.2",
"typescript": "^4.9.5",
"wireit": "^0.14.12"
"prettier": "^2.0.5",
"prettier-package-json": "^2.1.3",
"remark-html": "^13.0.1",
"rimraf": "^2.6.3",
"rollup": "^2.0.0",
"semver": "^7.5.2",
"sinon": "^7.2.2",
"ssl-root-cas": "^1.3.1",
"typescript": "~4.8.4",
"whatwg-fetch": "^3.0.0",
"wireit": "^0.7.2"
},
"bundlesize": [
{
@ -102,71 +128,23 @@
"maxSize": "20 kB"
}
],
"comments": {
"overrides": {
"//": [
"For best compatibility, we sometimes need to override a package version depended upon by multiple packages."
],
"sharp": [
"Version of 'sharp' package we get via '@rocket/cli' is too old to be built on a Mac.",
"But updating package '@rocket-cli' causes our portal to break. Reason unknown.",
"So, we override the sharp version in here until '@rocket-cli' can be updated."
]
},
"olderVersions": {
"//": [
"Our aim is to keep all depencies up-to-date(for maintainability, performance and security).",
"We use [npm-outdated](https://marketplace.visualstudio.com/items?itemName=mskelton.npm-outdated) as a helper for this.",
"Sometimes we can't bc/o incompatibility issues."
],
"eslint": [
"Can't be updated yet to 9.x, because of eslint-plugin-import"
],
"typescript": [
"Since changes in types can be reflected in the code, we want to keep this stable for a longer period of time.",
"As semver is not followed, we keep our major versions aligned with a minot of TS (hence '~' instead of '^' is used)"
],
"remark-html": [
"Can't be updated to 14.x, because remark-html is still in commonjs."
],
"@rocket/*": [
"Pinned, as newer versions require a complete overhaul of docs. Later we will move to astro."
],
"looks-same": [
"Part of ./scripts/screenshots, which is not incorporated atm in automated test suite. TODO: re-evaluate solution and whether visual regression testing needs to be part of this repo"
],
"@open-wc/building-rollup": [
"Can't be updated to 3.x, as v2 seems to be better compatible with rocket setup"
],
"chai": [
"Can't be updated to 5.x, because @web/dev-server-core test-helpers (having implicit dep on chai) is not compatible with chai >= 5.x"
]
},
"toBeRemoved": {
"//": [
"For maintainability, performance and security, we want to keep the number of dependencies as low as possible (in case functionality can be achieved via existing dependencies or platform functionality)."
],
"@bundled-es-modules/fetch-mock": [
"Can be achieved via sinon as well"
],
"publish-docs/fs-extra | @types/fs-extra": [
"Copy can be achieved via node's fs module"
],
"@web/dev-server-legacy": [
"Only needed for browserstack config. Can be achieved via @web/dev-server"
],
"@lit/reactive-element": [
"We make sure latest version is hoisted and resolved, until we move to astro (when we can regenerate the package-lock.json file w/o breaking Rocket)."
]
}
},
"overrides": {
"sharp": "^0.29.x"
"sharp": "^0.31.x"
},
"prettier": {
"printWidth": 100,
"singleQuote": true,
"arrowParens": "avoid",
"trailingComma": "all"
},
"watch": {
"build-docs:watch": {
"patterns": [
"docs"
],
"extensions": [
"*"
]
}
}
}

View file

@ -0,0 +1,27 @@
diff --git a/node_modules/@astrojs/markdown-remark/dist/rehype-collect-headings.js b/node_modules/@astrojs/markdown-remark/dist/rehype-collect-headings.js
index 2f1ee5d..6865631 100644
--- a/node_modules/@astrojs/markdown-remark/dist/rehype-collect-headings.js
+++ b/node_modules/@astrojs/markdown-remark/dist/rehype-collect-headings.js
@@ -1,10 +1,13 @@
import Slugger from "github-slugger";
import { visit } from "unist-util-visit";
+import * as path from 'path';
import { InvalidAstroDataError, safelyGetAstroData } from "./frontmatter-injection.js";
const rawNodeTypes = /* @__PURE__ */ new Set(["text", "raw", "mdxTextExpression"]);
const codeTagNames = /* @__PURE__ */ new Set(["code", "pre"]);
function rehypeHeadingIds() {
return function(tree, file) {
+ const fileName = file.history[0];
+ const parentDirectoryName = path.basename(path.dirname(fileName));
const headings = [];
const slugger = new Slugger();
const isMDX = isMDXFile(file);
@@ -52,7 +55,7 @@ function rehypeHeadingIds() {
let slug = slugger.slug(text);
if (slug.endsWith("-"))
slug = slug.slice(0, -1);
- node.properties.id = slug;
+ node.properties.id = `${parentDirectoryName}-${slug}`;
}
headings.push({ depth, slug: node.properties.id, text });
});

View file

@ -0,0 +1,275 @@
diff --git a/node_modules/@mdjs/core/src/mdjsParse.js b/node_modules/@mdjs/core/src/mdjsParse.js
index 730e32a..5d6f3f5 100644
--- a/node_modules/@mdjs/core/src/mdjsParse.js
+++ b/node_modules/@mdjs/core/src/mdjsParse.js
@@ -5,13 +5,12 @@ import { remove } from 'unist-util-remove';
/** @typedef {import('unist').Node} Node */
export function mdjsParse() {
- let jsCode = '';
-
/**
* @param {Node} tree
* @param {VFileOptions} file
*/
function transformer(tree, file) {
+ let jsCode = '';
visit(
tree,
'code',
diff --git a/node_modules/@mdjs/core/src/mdjsSetupCode.js b/node_modules/@mdjs/core/src/mdjsSetupCode.js
index 02a7c56..db3e466 100644
--- a/node_modules/@mdjs/core/src/mdjsSetupCode.js
+++ b/node_modules/@mdjs/core/src/mdjsSetupCode.js
@@ -76,10 +76,6 @@ export function mdjsSetupCode({
` 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');
}
diff --git a/node_modules/@mdjs/core/src/mdjsStoryParse.js b/node_modules/@mdjs/core/src/mdjsStoryParse.js
index fe734a3..3ddd3a1 100644
--- a/node_modules/@mdjs/core/src/mdjsStoryParse.js
+++ b/node_modules/@mdjs/core/src/mdjsStoryParse.js
@@ -54,132 +54,130 @@ export function mdjsStoryParse({
storyTag = defaultStoryTag,
previewStoryTag = defaultPreviewStoryTag,
} = {}) {
- /** @type {Story[]} */
- const stories = [];
- let htmlIndex = 0;
-
- /* eslint-disable no-param-reassign */
-
/**
- * @param {UnistNode} _node
- * @param {number} index
- * @param {UnistParent} parent
+ * @param {Node} tree
+ * @param {VFileOptions} file
*/
- const nodeCodeVisitor = (_node, index, parent) => {
- let node = /** @type {UnistNode & {[key: string]: unknown}} */ (_node);
- if (node.lang === 'js' && node.meta === 'story' && typeof node.value === 'string') {
- const storyData = extractStoryData(node.value);
- node.type = 'html';
- node.value = storyTag(storyData.name);
- stories.push(storyData);
- }
- if (node.lang === 'js' && node.meta === 'preview-story' && typeof node.value === 'string') {
- const storyData = extractStoryData(node.value);
- const newValue = previewStoryTag(storyData.name);
- if (newValue.includes('[[CODE SLOT]]')) {
- const tagParts = newValue.split('[[CODE SLOT]]');
-
- const inside = [node];
- let skipAmount = 1;
+ async function transformer(tree, file) {
+ /** @type {Story[]} */
+ const stories = [];
+ let htmlIndex = 0;
+
+ /**
+ * @param {UnistNode} _node
+ * @param {number} index
+ * @param {UnistParent} parent
+ */
+ const nodeCodeVisitor = (_node, index, parent) => {
+ let node = /** @type {UnistNode & {[key: string]: unknown}} */ (_node);
+ if (node.lang === 'js' && node.meta === 'story' && typeof node.value === 'string') {
+ const storyData = extractStoryData(node.value);
+ node.type = 'html';
+ node.value = storyTag(storyData.name);
+ stories.push(storyData);
+ }
+ if (node.lang === 'js' && node.meta === 'preview-story' && typeof node.value === 'string') {
+ const storyData = extractStoryData(node.value);
+ const newValue = previewStoryTag(storyData.name);
+ if (newValue.includes('[[CODE SLOT]]')) {
+ const tagParts = newValue.split('[[CODE SLOT]]');
- const next = /** @type {UnistNode & {[key: string]: unknown}} */ (
- parent.children[index + 1]
- );
- if (next && next.type === 'code' && next.meta === 'story-code') {
- inside.push(next);
- skipAmount += 1;
+ const inside = [node];
+ let skipAmount = 1;
- const next2 = /** @type {UnistNode & {[key: string]: unknown}} */ (
- parent.children[index + 2]
+ const next = /** @type {UnistNode & {[key: string]: unknown}} */ (
+ parent.children[index + 1]
);
- if (next2 && next2.type === 'code' && next2.meta === 'story-code') {
- inside.push(next2);
+ if (next && next.type === 'code' && next.meta === 'story-code') {
+ inside.push(next);
skipAmount += 1;
+
+ const next2 = /** @type {UnistNode & {[key: string]: unknown}} */ (
+ parent.children[index + 2]
+ );
+ if (next2 && next2.type === 'code' && next2.meta === 'story-code') {
+ inside.push(next2);
+ skipAmount += 1;
+ }
}
+
+ node = {
+ type: 'root',
+ children: [
+ { type: 'html', value: tagParts[0] },
+ { type: 'text', value: '\n\n' },
+ ...inside,
+ { type: 'text', value: '\n\n' },
+ { type: 'html', value: tagParts[1] },
+ ],
+ };
+ parent.children.splice(index, skipAmount, node);
+ } else {
+ node.type = 'html';
+ node.value = previewStoryTag(storyData.name);
}
- node = {
- type: 'root',
- children: [
- { type: 'html', value: tagParts[0] },
- { type: 'text', value: '\n\n' },
- ...inside,
- { type: 'text', value: '\n\n' },
- { type: 'html', value: tagParts[1] },
- ],
- };
- parent.children.splice(index, skipAmount, node);
- } else {
- node.type = 'html';
- node.value = previewStoryTag(storyData.name);
+ stories.push(storyData);
}
- stories.push(storyData);
- }
-
- if (node.lang === 'html' && node.meta === 'story') {
- const storyData = extractStoryData(
- `export const HtmlStory${htmlIndex} = () => html\`${node.value}\`;`,
- { type: 'html' },
- );
- node.type = 'html';
- node.value = storyTag(storyData.name);
- stories.push(storyData);
- htmlIndex += 1;
- }
- if (node.lang === 'html' && node.meta === 'preview-story') {
- const storyData = extractStoryData(
- `export const HtmlStory${htmlIndex} = () => html\`${node.value}\`;`,
- { type: 'html' },
- );
-
- const newValue = previewStoryTag(storyData.name);
- if (newValue.includes('[[CODE SLOT]]')) {
- const tagParts = newValue.split('[[CODE SLOT]]');
- const inside = [node];
- let skipAmount = 1;
- const next = /** @type {UnistNode & {[key: string]: unknown}} */ (
- parent.children[index + 1]
+ if (node.lang === 'html' && node.meta === 'story') {
+ const storyData = extractStoryData(
+ `export const HtmlStory${htmlIndex} = () => html\`${node.value}\`;`,
+ { type: 'html' },
+ );
+ node.type = 'html';
+ node.value = storyTag(storyData.name);
+ stories.push(storyData);
+ htmlIndex += 1;
+ }
+ if (node.lang === 'html' && node.meta === 'preview-story') {
+ const storyData = extractStoryData(
+ `export const HtmlStory${htmlIndex} = () => html\`${node.value}\`;`,
+ { type: 'html' },
);
- if (next && next.type === 'code' && next.meta === 'story-code') {
- inside.push(next);
- skipAmount += 1;
- const next2 = /** @type {UnistNode & {[key: string]: unknown}} */ (
- parent.children[index + 2]
+ const newValue = previewStoryTag(storyData.name);
+ if (newValue.includes('[[CODE SLOT]]')) {
+ const tagParts = newValue.split('[[CODE SLOT]]');
+ const inside = [node];
+ let skipAmount = 1;
+ const next = /** @type {UnistNode & {[key: string]: unknown}} */ (
+ parent.children[index + 1]
);
- if (next2 && next2.type === 'code' && next2.meta === 'story-code') {
- inside.push(next2);
+ if (next && next.type === 'code' && next.meta === 'story-code') {
+ inside.push(next);
skipAmount += 1;
+
+ const next2 = /** @type {UnistNode & {[key: string]: unknown}} */ (
+ parent.children[index + 2]
+ );
+ if (next2 && next2.type === 'code' && next2.meta === 'story-code') {
+ inside.push(next2);
+ skipAmount += 1;
+ }
}
+
+ node = {
+ type: 'root',
+ children: [
+ { type: 'html', value: tagParts[0] },
+ { type: 'text', value: '\n\n' },
+ ...inside,
+ { type: 'text', value: '\n\n' },
+ { type: 'html', value: tagParts[1] },
+ ],
+ };
+ parent.children.splice(index, skipAmount, node);
+ } else {
+ node.type = 'html';
+ node.value = previewStoryTag(storyData.name);
}
- node = {
- type: 'root',
- children: [
- { type: 'html', value: tagParts[0] },
- { type: 'text', value: '\n\n' },
- ...inside,
- { type: 'text', value: '\n\n' },
- { type: 'html', value: tagParts[1] },
- ],
- };
- parent.children.splice(index, skipAmount, node);
- } else {
- node.type = 'html';
- node.value = previewStoryTag(storyData.name);
+ stories.push(storyData);
+ htmlIndex += 1;
}
+ };
- stories.push(storyData);
- htmlIndex += 1;
- }
- };
-
- /**
- * @param {Node} tree
- * @param {VFileOptions} file
- */
- async function transformer(tree, file) {
// unifiedjs expects node changes to be made on the given node...
await init;
// @ts-ignore

48
patches/README.md Normal file
View file

@ -0,0 +1,48 @@
## @mdjs/core@0.20.0
### mdjsStoryParse.js
The original file URL is here: [URL](https://github.com/modernweb-dev/rocket/blob/%40mdjs/core%400.20.0/packages/mdjs-core/src/mdjsStoryParse.js)
#### Why
Astro does not call [mdjsStoryParse](https://github.com/modernweb-dev/rocket/blob/%40mdjs/core%400.20.0/packages/mdjs-core/src/mdjsStoryParse.js#L53) every time an md file is changed while `watching`. The function is called only once. However some [shared variables](https://github.com/modernweb-dev/rocket/blob/%40mdjs/core%400.20.0/packages/mdjs-core/src/mdjsStoryParse.js#L58-L59) are set on the level of the `mdjsStoryParse`. That leads to the situation that those variable are shared among all md files which is not according to the design. The orignal idea is to share those per an md file. As a result when generating `__mdjs-stories.js` files, they get polluted with the data from other files.
#### About the fix
- [nodeCodeVisitor](https://github.com/modernweb-dev/rocket/blob/%40mdjs/core%400.20.0/packages/mdjs-core/src/mdjsStoryParse.js#L68) function was moved under the [transformer](https://github.com/modernweb-dev/rocket/blob/%40mdjs/core%400.20.0/packages/mdjs-core/src/mdjsStoryParse.js#L182C18-L182C29) function
- [Shared variables](https://github.com/modernweb-dev/rocket/blob/%40mdjs/core%400.20.0/packages/mdjs-core/src/mdjsStoryParse.js#L58-L59) were moved under the `transformer` function
This way the shared variables instantiated on every `transformer` function call.
### mdjsParse.js
#### Why patching
Astro does not call [mdjsParse](https://github.com/modernweb-dev/rocket/blob/%40mdjs/core%400.20.0/packages/mdjs-core/src/mdjsParse.js#L7) every time an md file is changed while `watching`. The function is called only once. However some [shared variables](https://github.com/modernweb-dev/rocket/blob/%40mdjs/core%400.20.0/packages/mdjs-core/src/mdjsParse.js#L8) are set on the level of the `mdjsParse`. That leads to the situation that those variable are shared among all md files which is not according to the design. The orignal idea is to share those per an md file. As a result when generating `__mdjs-stories.js` files, they get polluted with the data from other files.
#### About the patch
- [Shared variables](https://github.com/modernweb-dev/rocket/blob/%40mdjs/core%400.20.0/packages/mdjs-core/src/mdjsParse.js#L8) were moved under the `transformer` function
This way the shared variables instantiated on every `transformer` function call.
### mdjsSetupCode.js
Dynamic `imports` for `@mdjs/mdjs-preview/define` and `@mdjs/mdjs-story/define` were removed. These imports are inlined into `__mdjs-story.js` by `copyMdjsStories.js` remark plugin. This is done to enable `dist` bundling.
## @astrojs/markdown-remark
The patch is done to inhance the `id` naming of <H> tag `HTML` elements.
### Why updating naming
In the current implementation of the portal we need to concatenate `md` pages. Astro creates unique values for the `id` attributes for `<H>` tags (`h1`, `h2`, etc. ). The problem is that the `id`'s are not longer unique after concatenation. There might be multiple `overview` `id`'s which is not correct for navigation.
### What the solution is about
The solution is to add the parent directory name to the each <H> id as a prefix. That is if the `md` file being parsed is called `docs/tools/my.md` and let's say there is an `h2` tag in with id called `overview`, then after applying patch, the new `id` value becomes `tools-overview`.
## lit
The patch is required to make `astro build` work correctly. `lit` is added as an `external` library for the build option in `astro.config.mjs`. And without the patch the build throws errors.

8
patches/lit+2.8.0.patch Normal file
View file

@ -0,0 +1,8 @@
diff --git a/node_modules/lit/index.js b/node_modules/lit/index.js
index 599d7d0..68a9dcb 100644
--- a/node_modules/lit/index.js
+++ b/node_modules/lit/index.js
@@ -1,2 +1,2 @@
-import"@lit/reactive-element";import"lit-html";export*from"lit-element/lit-element.js";export*from"lit-html/is-server.js";
+import"@lit/reactive-element";import"lit-html";export { nothing } from 'lit-html';export*from"lit-element/lit-element.js";export*from"lit-html/is-server.js";
//# sourceMappingURL=index.js.map

9
public/favicon.svg Normal file
View file

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
<style>
path { fill: #000; }
@media (prefers-color-scheme: dark) {
path { fill: #FFF; }
}
</style>
</svg>

After

Width:  |  Height:  |  Size: 749 B

9
rollup.config-test.js Normal file
View file

@ -0,0 +1,9 @@
import nodeResolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
export default {
plugins: [
commonjs(), // <-- this handles some parsing of js syntax or something (necessary for `export { init } from "mathjax";`)
nodeResolve(), // <-- this allows npm modules to be added to bundle
],
};

View file

@ -0,0 +1,13 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { LitElement } from 'lit';
/**
* @typedef {import('./UIBaseElementTypes').UIBaseElementInterface} UIBaseElementInterface
*/
/**
* @type {UIBaseElementInterface}
*/
export class UIBaseElement extends LitElement {
static templates = {};
}

View file

@ -0,0 +1,41 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { LitElement, html, nothing, css } from 'lit';
const tagName = 'ui-portal-footer';
export class UIPortalFooter extends LitElement {
static properties = {
footerData: { type: Array, attribute: 'footer-data' },
};
static styles = [
css`
footer > ul {
display: flex;
gap: 8em;
}
`,
];
constructor() {
super();
this.footerData = [];
}
render() {
return html` <footer>${this._renderNavLevel({ children: this.footerData })}</footer> `;
}
_renderNavLevel({ children }) {
return html`<ul>
${children.map(
item => html`<li>
<a href="${item.url}">${item.name}</a>
${item.children?.length ? this._renderNavLevel({ children: item.children }) : nothing}
</li>`,
)}
</ul>`;
}
}
customElements.define(tagName, UIPortalFooter);

View file

@ -0,0 +1,32 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { LitElement, html, nothing } from 'lit';
const tagName = 'ui-portal-inpage-nav';
export class UIPortalInpageNav extends LitElement {
static properties = {
navData: { type: Array, attribute: 'nav-data' },
};
constructor() {
super();
this.navData = [];
}
render() {
return html` <nav>${this._renderNavLevel({ children: this.navData })}</nav> `;
}
_renderNavLevel({ children }) {
return html`<ul>
${children.map(
item => html`<li>
<a href="${item.url}">${item.name}</a>
${item.children?.length ? this._renderNavLevel({ children: item.children }) : nothing}
</li>`,
)}
</ul>`;
}
}
customElements.define(tagName, UIPortalInpageNav);

View file

@ -0,0 +1,34 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { LitElement, html, nothing } from 'lit';
const tagName = 'ui-portal-main-nav';
// TODO: apply https://web.dev/website-navigation/ (aria-current="page" etc.)
export class UiPortalMainNav extends LitElement {
static properties = {
navData: { type: Array, attribute: 'nav-data' },
};
constructor() {
super();
this.navData = [];
}
render() {
return html` <nav>${this._renderNavLevel({ children: this.navData })}</nav> `;
}
_renderNavLevel({ children }) {
return html`<ul>
${children.map(
item => html`<li>
<a href="${item.url}">${item.name}</a>
${item.children?.length ? this._renderNavLevel({ children: item.children }) : nothing}
</li>`,
)}
</ul>`;
}
}
customElements.define(tagName, UiPortalMainNav);

21
src/content.ts Normal file
View file

@ -0,0 +1,21 @@
import { getCollection } from 'astro:content';
import {
isBlogEntry,
isComponentInfoEntry,
isComponentPageEntry,
isFundamentalsEntry,
isGuideEntry
} from './content/config';
export const allPages = await getCollection('docs');
export const componentInfoEntries = allPages.filter(isComponentInfoEntry);
export const componentPageEntries = allPages.filter(isComponentPageEntry);
export const blogEntries = allPages.filter(isBlogEntry);
export const guideEntries = allPages.filter(isGuideEntry);
export const fundamentalsEntries = allPages.filter(isFundamentalsEntry);
export const getInfoParentSlug = entry => entry.slug.split('/info')[0];
export const getComponentEntries = (componentName) => {
return componentPageEntries.filter(componentEntry => componentEntry.data.component === componentName);
}

35
src/content/config.ts Normal file
View file

@ -0,0 +1,35 @@
import { z, defineCollection } from "astro:content";
const componentSchema = z.object({
component: z.string(),
title: z.string().optional(),
description: z.string().optional()
});
const anySchema = z.object({
any: z.string().optional(),
});
const blogSchema = z.object({
title: z.string(),
description: z.string(),
published: z.boolean(),
});
export const docsCollectionSchema = z.union([
blogSchema,
componentSchema,
anySchema,
]);
const docs = defineCollection({
schema: docsCollectionSchema,
});
export const isComponentInfoEntry = (entry) => entry.slug.startsWith('components') && entry.slug.endsWith('/info');
export const isComponentPageEntry = (entry) => entry.slug.startsWith('components') && !entry.slug.endsWith('/info');
export const isBlogEntry = (entry) => entry.slug.startsWith('blog');
export const isFundamentalsEntry = (entry) => entry.slug.startsWith('fundamentals');
export const isGuideEntry = (entry) => entry.slug.startsWith('guides');
export const collections = { docs };

View file

@ -0,0 +1,157 @@
---
import { getCollection } from "astro:content";
import { SEO } from "astro-seo";
import { UIPortalFooter } from "../components/ui-portal-footer.js";
import { UiPortalMainNav } from "../components/ui-portal-main-nav.js";
import { componentInfoEntries } from '../content';
const site = Astro.site || Astro.url.origin;
const canonicalURL = new URL(Astro.url.pathname, site).toString();
const resolvedImageWithDomain = new URL("/opengraph.jpg", site).toString();
const { title } = Astro.props;
const compNames = componentInfoEntries.map((entry) => entry.data.component);
const navData = [
{ name: "home", url: "/" },
{
name: "components",
url: "/components",
children: compNames.map((compName) => ({
name: compName,
url: `/components/${compName}`,
})),
},
{ name: "blog", url: "/blog" },
{ name: "fundamentals", url: "/fundamentals" },
{ name: "guides", url: "/guides" },
];
const footerData = [
{
name: "discover",
children: [
{
name: "blog",
url: "/blog",
},
{
name: "help and feedback",
url: "https://github.com/ing-bank/lion/issues",
},
],
},
{
name: "follow",
children: [
{
name: "github",
url: "https://github.com/ing-bank/lion",
},
{
name: "slack",
url: "/about/slack/",
},
],
},
{
name: "support",
children: [
{
name: "contribute",
url: "https://github.com/ing-bank/lion/blob/master/CONTRIBUTING.md",
},
],
},
];
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<link
href="http://fonts.googleapis.com/css?family=Roboto"
rel="stylesheet"
type="text/css"
/>
<style>
body {
font-family: "Roboto", sans-serif;
font-size: 16px;
}
</style>
<!-- <link rel="preload" as="image" href={src} alt="Hero" /> -->
<SEO
title={title}
description="High quality UI web components"
canonical={canonicalURL}
openGraph={{
basic: {
url: canonicalURL,
type: "website",
title: "Lion",
image: resolvedImageWithDomain,
},
image: {
alt: "Lion Homepage screen",
},
}}
/>
</head>
<body class="ui-portal-grid">
<div class="ui-portal-grid__nav">
<UiPortalMainNav nav-data={JSON.stringify(navData)} />
</div>
<main class="ui-portal-grid__main">
<slot />
</main>
<div class="ui-portal-grid__footer">
<UIPortalFooter footer-data={JSON.stringify(footerData)} />
</div>
<style is:global>
/* Improve Page speed */
/* https://css-tricks.com/almanac/properties/c/content-visibility/ */
img {
content-visibility: auto;
}
.ui-portal-grid {
display: grid;
grid-template-columns: 300px 1fr;
grid-template-rows: 80vh 20vh;
grid-template-areas:
"nav main"
"nav footer";
}
.ui-portal-grid__nav {
grid-area: nav;
}
.ui-portal-grid__main {
grid-area: main;
}
.ui-portal-grid__footer {
grid-area: footer;
}
.ui-portal-collection {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 300px));
}
.ui-portal-card {
display: flex;
flex-direction: column;
padding: 1rem;
border-radius: 0.5rem;
}
</style>
</body>
</html>

View file

@ -0,0 +1,4 @@
---
---
<slot />

View file

@ -0,0 +1,88 @@
---
import { glob } from 'glob'
import * as fs from 'fs';
import * as process from 'process';
import MainLayout from '../../layouts/MainLayout.astro';
import { UIPortalInpageNav } from "../../components/ui-portal-inpage-nav.js";
import * as path from 'path';
import { fundamentalsEntries, allPages } from '../../content';
import { blogEntries } from '../../content';
import { guideEntries } from '../../content';
import { maxDepthForNonComponentsNavigation } from '../../../config.mjs';
import { getPages, getInPageNavData, getPagesByDir, getPathForMdjsStroriesFile } from '../../utils/pages/render-entries.ts';
export async function getStaticPaths() {
const fundamentalsArr = fundamentalsEntries.map(entry => ({
params: {
top: 'fundamentals',
slug: entry.slug.split('fundamentals/')[1]
},
props: { entry },
}));
const blogArr = blogEntries.map(entry => ({
params: {
top: 'blog',
slug: entry.slug.split('blog/')[1]
},
props: { entry },
}));
const guideArr = guideEntries .map(entry => ({
params: {
top: 'guides',
slug: entry.slug.split('guides/')[1]
},
props: { entry },
}));
const dirArr = allPages
.filter(entry => {
const separator = '/';
const dirPath = path.dirname(entry.slug);
const dirParts = dirPath.split(separator);
return dirParts.length >= maxDepthForNonComponentsNavigation && dirParts[0] !== 'components';
})
.map(entry => {
const separator = '/';
const dirPath = path.dirname(entry.slug);
const dirParts = dirPath.split(separator);
dirParts.splice(maxDepthForNonComponentsNavigation);
const result = {
params: {
top: dirParts.shift(),
slug: dirParts.join(separator)
},
};
return result;
});
const uniqueDirArr = [];
dirArr.forEach(route => {
if (!uniqueDirArr.find(uniqueRoute => uniqueRoute.params.top === route.params.top && uniqueRoute.params.slug === route.params.slug)) {
uniqueDirArr.push(route);
}
});
return [...fundamentalsArr, ...blogArr, ...guideArr, ...dirArr];
}
const { entry } = Astro.props;
// URL path as a base for in-page navigation. It is used to add anchors to. F.e. "host://urlPath#header1"
const urlPath = entry ? entry.slug : path.join(Astro.params.top, Astro.params.slug);
const pages = entry ? await getPages([entry]) : await getPagesByDir(urlPath);
const pathForMdjsStroriesFile = await getPathForMdjsStroriesFile(!entry, urlPath);
---
<MainLayout title={entry?.slug}>
<UIPortalInpageNav nav-data={JSON.stringify(getInPageNavData(pages, urlPath))} />
{
pages.map((page) => (
<page.Content />
))
}
<script is:inline src="/docs/_assets/scoped-custom-element-registry.min.js"></script>
{pathForMdjsStroriesFile && <script type="module" src={pathForMdjsStroriesFile} mdjs-setup></script>}
</MainLayout>

View file

@ -0,0 +1,36 @@
---
import MainLayout from "../../layouts/MainLayout.astro";
import { fundamentalsEntries } from "../../content";
import { blogEntries } from "../../content";
import { guideEntries } from "../../content";
export async function getStaticPaths() {
return [{
params: { top: 'fundamentals' },
props: { entries: fundamentalsEntries },
}, {
params: { top: 'blog' },
props: { entries: blogEntries },
}, {
params: { top: 'guides' },
props: { entries: guideEntries },
}];
}
const entries = Astro.props.entries;
---
<MainLayout>
<div class="ui-portal-collection">
{
entries.map((entry) => (
<article class="ui-portal-card">
<h2>{entry.data.title}</h2>
<p>{entry.slug}</p>
<p>{entry.data.description}</p>
<a href={entry.slug}>View blog</a>
</article>
))
}
</div>
</MainLayout>

View file

@ -0,0 +1,35 @@
---
import * as path from 'path';
import { componentInfoEntries, getComponentEntries } from '../../content';
import MainLayout from "../../layouts/MainLayout.astro";
import ComponentLayout from "../../layouts/partials/ComponentPartial.astro";
import { UIPortalInpageNav } from "../../components/ui-portal-inpage-nav.js";
import { getPages, getInPageNavData, getPagesByDir, getPathForMdjsStroriesFile } from '../../utils/pages/render-entries.ts';
export async function getStaticPaths() {
return componentInfoEntries.map((entry) => ({
params: {
component: entry.data.component
},
props: { entry }
}));
}
const { entry } = Astro.props;
const urlPath = path.dirname(entry.slug);
const pages = await getPagesByDir(urlPath);
const pathForMdjsStroriesFile = await getPathForMdjsStroriesFile(true, urlPath);
---
<MainLayout title={entry.slug}>
<ComponentLayout frontmatter={entry.data}>
<UIPortalInpageNav nav-data={JSON.stringify(getInPageNavData(pages, urlPath))} />
{
pages.map((page) => (
<page.Content />
))
}
<script is:inline src="/docs/_assets/scoped-custom-element-registry.min.js"></script>
{pathForMdjsStroriesFile && <script type="module" src={pathForMdjsStroriesFile} mdjs-setup></script>}
</ComponentLayout>
</MainLayout>

View file

@ -0,0 +1,18 @@
---
import MainLayout from "../../layouts/MainLayout.astro";
import { componentInfoEntries, getInfoParentSlug } from '../../content';
---
<MainLayout>
<div class="ui-portal-collection">
{
componentInfoEntries.map((comp) => (
<article class="ui-portal-card">
<h2>{comp.data.component}</h2>
<p>{comp.data.description}</p>
<a href={`/${getInfoParentSlug(comp)}`}>View comp</a>
</article>
))
}
</div>
</MainLayout>

7
src/pages/index.astro Normal file
View file

@ -0,0 +1,7 @@
---
import MainLayout from "../layouts/MainLayout.astro";
---
<MainLayout>
<h1>Home, sweet home</h1>
</MainLayout>

89
src/pages/simulator.astro Normal file
View file

@ -0,0 +1,89 @@
<html theme="light" platform="web" lang="en"><head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
<style type="text/css">
body {
margin: 0;
height: fit-content;
}
html[edge-distance] body {
padding: 8px;
}
</style>
<script type="module">
import { render } from '/node_modules/@mdjs/mdjs-story/index.js';
function sanitize(input, type) {
return `${document.location.origin}/${input.match(/[a-zA-Z0-9\/\-_]*/)[0]}.${type}`;
}
async function onHashChange() {
const urlParts = new URLSearchParams(document.location.hash.substr(1));
if (urlParts.get('stylesheets')) {
for (const stylesheet of urlParts.getAll('stylesheets')) {
const safeStylesheetUrl = sanitize(stylesheet, 'css');
if (!document.querySelector(`link[rel="stylesheet"][href="${safeStylesheetUrl}"]`)) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = safeStylesheetUrl;
document.head.appendChild(link);
}
}
}
if (urlParts.get('moduleUrls')) {
for (const moduleUrl of urlParts.getAll('moduleUrls')) {
const safeModuleUrl = sanitize(moduleUrl, 'js');
if (!document.querySelector(`script[type=module][src="${safeModuleUrl}"]`)) {
const script = document.createElement('script');
script.type = 'module';
debugger;
script.src = safeModuleUrl;
document.head.appendChild(script);
}
}
}
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 safeStoryUrl = sanitize(urlParts.get('story-file'), 'js');
const mod = await import(safeStoryUrl);
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>

View file

@ -0,0 +1,240 @@
const path = require('path');
const fs = require('fs/promises');
const process = require('process');
// eslint-disable-next-line import/no-extraneous-dependencies
const imageExtensions = require('image-extensions');
// eslint-disable-next-line import/no-extraneous-dependencies
const findNodeModules = require('find-node-modules');
// eslint-disable-next-line import/no-extraneous-dependencies
const { init, parse } = require('es-module-lexer');
const childProcess = require('child_process');
const sourceDocsPath = 'docs';
const contentPathForDocs = 'src/content';
const contentDocsPath = `${contentPathForDocs}/docs`;
const publicPathForDocs = 'public';
const publicDocsPath = `${publicPathForDocs}/docs`;
const rootPath = process.cwd();
const isDistBuild = process.env.PROD === 'true';
async function processImportsForFile(filePath) {
if (filePath.includes('__mdjs-stories')) {
return;
}
const source = await fs.readFile(filePath, 'utf8');
if (source === '' || !source.includes('import')) {
return;
}
const fileExtName = path.extname(filePath);
if (fileExtName !== '.js' && fileExtName !== '.mjs' && fileExtName !== '.ts') {
return;
}
let newSource = '';
let lastPos = 0;
await init;
const [imports] = parse(source);
for (const importObj of imports) {
newSource += source.substring(lastPos, importObj.s);
const importSrc = source.substring(importObj.s, importObj.e);
const isDynamicImport = importObj.d > -1;
if (
importSrc.startsWith('.') ||
importSrc.startsWith('/') ||
isDynamicImport ||
importSrc.startsWith('import.')
) {
newSource += importSrc;
} else {
const nodeModulesLocation = findNodeModules(filePath)[0];
const nodeModulesLocation1 = path.join(filePath, nodeModulesLocation);
const resolvedImportPath = require.resolve(importSrc, { paths: [nodeModulesLocation1] });
const lastNodeModulesDirectoryIndex = resolvedImportPath.lastIndexOf('nodeModulesText');
const nodeModulesPath = resolvedImportPath.substring(0, lastNodeModulesDirectoryIndex);
const dependencyPath = resolvedImportPath.substring(lastNodeModulesDirectoryIndex);
if (nodeModulesPath === rootPath) {
newSource += dependencyPath;
} else {
newSource += resolvedImportPath.split(rootPath)[1];
}
}
lastPos = importObj.e;
}
newSource += source.substring(lastPos, source.length);
await fs.writeFile(filePath, newSource);
}
async function createInfoMd(componentDirectoryPath) {
const componentName = path.basename(componentDirectoryPath);
const infoMd = `---
component: ${componentName}
title: ${componentName} title
description: ${componentName} description
---\n`;
const infoMdFilePath = path.join(componentDirectoryPath, 'info.md');
await fs.mkdir(componentDirectoryPath, { recursive: true });
await fs.writeFile(infoMdFilePath, infoMd);
}
function getFrontmatter(text) {
const result = {};
if (!text.startsWith('---')) {
return result;
}
const frontmatterRegex = /---\n(.+?)\n---/gs;
const match = text.matchAll(frontmatterRegex);
const matchResult = [...match]?.[0]?.[1] || '';
if (matchResult) {
matchResult.split('\n').forEach(pair => {
const pairArr = pair.split(':');
const key = pairArr[0].trim();
const value = pairArr[1].trim();
result[key] = value;
});
}
return result;
}
function getContent(text) {
if (Object.keys(getFrontmatter(text)).length === 0) {
return text;
}
const frontmatterRegex = /---\n(.+?)\n---\n(.*)/gs;
const match = text.matchAll(frontmatterRegex);
return [...match]?.[0]?.[2] || text;
}
function frontmatterToString(frontmatter) {
const prefix = '---\n';
let result = prefix;
Object.keys(frontmatter).forEach(key => {
result += `${key}: ${frontmatter[key]}\n`;
});
if (result === prefix) {
return '';
}
return `${result}---\n\n`;
}
function addFrontmatter(fileContent, key, value) {
const frontmatter = getFrontmatter(fileContent);
const content = getContent(fileContent);
if (value) {
frontmatter[key] = value;
}
return frontmatterToString(frontmatter) + content;
}
function getOrder(fileContent) {
const orderRegexp = /#.+\|\|(\d+)/gm;
const match = fileContent.matchAll(orderRegexp);
return [...match]?.[0]?.[1];
}
async function copyDocs(currentPath = '') {
const files = await fs.readdir(path.join(sourceDocsPath, currentPath));
for (const file of files) {
const sourceDocsFilePath = path.join(sourceDocsPath, currentPath, file);
const contentDocsFilePath =
file === 'index.md'
? path.join(contentDocsPath, currentPath, 'dir-index.md')
: path.join(contentDocsPath, currentPath, file);
const publicDocsFilePath = path.join(publicDocsPath, currentPath, file);
const stats = await fs.lstat(sourceDocsFilePath);
if (stats.isDirectory()) {
if (currentPath === 'components') {
await createInfoMd(contentDocsFilePath);
}
await copyDocs(path.join(currentPath, file));
} else {
if (
(path.extname(file) === '.md' && sourceDocsFilePath !== `${sourceDocsPath}/index.md`) ||
imageExtensions.includes(path.extname(file).split('.')[1])
) {
await fs.mkdir(path.join(contentDocsPath, currentPath), { recursive: true });
await fs.copyFile(sourceDocsFilePath, contentDocsFilePath);
if (path.extname(file) === '.md') {
const fileContent = await fs.readFile(contentDocsFilePath, 'utf8');
let updatedFileContent = fileContent;
if (contentDocsFilePath.includes('/components')) {
const parentComponent = path.basename(currentPath);
updatedFileContent = addFrontmatter(fileContent, 'component', parentComponent);
}
const order = getOrder(fileContent);
updatedFileContent = addFrontmatter(updatedFileContent, 'order', order);
await fs.writeFile(contentDocsFilePath, updatedFileContent);
}
}
if (path.extname(file) !== '.md') {
await fs.mkdir(path.join(publicDocsPath, currentPath), { recursive: true });
await fs.copyFile(sourceDocsFilePath, publicDocsFilePath);
}
}
}
}
async function copyDocsByFileArray(files) {
for (const file of files) {
const currentPath = path.dirname(file);
const contentDocsFilePath = path.join(contentPathForDocs, file);
const publicDocsFilePath = path.join(publicPathForDocs, file);
if (path.extname(file) === '.md' && file !== 'index.md') {
await fs.mkdir(path.join(contentPathForDocs, currentPath), { recursive: true });
await fs.copyFile(file, contentDocsFilePath);
if (file.includes('/components')) {
const fileContent = await fs.readFile(contentDocsFilePath, 'utf8');
const parentComponent = path.basename(currentPath);
let updatedFileContent = fileContent;
updatedFileContent = addFrontmatter(fileContent, 'component', parentComponent);
const order = getOrder(fileContent);
updatedFileContent = addFrontmatter(updatedFileContent, 'order', order);
await fs.writeFile(contentDocsFilePath, updatedFileContent);
}
}
if (path.extname(file) !== '.md') {
await fs.mkdir(path.join(publicPathForDocs, currentPath), { recursive: true });
await fs.copyFile(file, publicDocsFilePath);
await processImportsForFile(publicDocsFilePath);
}
}
}
async function processImports(currentPath = '') {
if (isDistBuild) {
return;
}
const files = await fs.readdir(path.join(publicDocsPath, currentPath));
for (const file of files) {
const publicDocsFilePath = path.join(publicDocsPath, currentPath, file);
const stats = await fs.stat(publicDocsFilePath);
if (stats.isDirectory()) {
if (!file.startsWith('_')) {
await processImports(path.join(currentPath, file));
}
} else {
await processImportsForFile(publicDocsFilePath);
}
}
}
async function watch() {
const diffStr = childProcess.execSync('git diff --name-only "docs"').toString();
const diffArr = diffStr.split('\n').filter(value => value);
if (diffArr.length) {
// eslint-disable-next-line no-console
console.log('Files have been changed: ', diffArr);
}
await copyDocsByFileArray(diffArr);
}
module.exports = {
copyDocs,
processImports,
watch,
};

View file

@ -0,0 +1,7 @@
const { copyDocs, processImports, watch } = require('./copy-docs.js');
module.exports = {
copyDocs,
processImports,
watch,
};

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1,197 @@
import { glob } from 'glob'
import * as fs from 'fs';
import * as process from 'process';
import * as path from 'path';
import { fundamentalsEntries, allPages } from '../../content';
import { maxDepthForNonComponentsNavigation } from '../../../config.mjs';
const convertHeadingsToInPageNavData = (headings, urlPath) => {
return headings.map(header => {
const anchor = header.slug;
return {
name: header.text,
url: `/${urlPath}#${anchor}`
};
});
};
const parseEntries = async (entries) => {
const contents = [];
for (const componentEntry of entries) {
const { Content, headings, remarkPluginFrontmatter } = await componentEntry.render();
const order = remarkPluginFrontmatter.order;
const slug = componentEntry.slug;
const content = {Content, headings, order, slug};
contents.push(content);
}
return contents;
};
export const getInPageNavData = (contentItems, urlPath) => {
const inPageNavData = [];
const parentDirToNavDataMap = new Map();
const arePagesConcatenated = contentItems.length > 1;
for (const contentItem of contentItems) {
const headersH2 = contentItem.headings.filter(header => header.depth === 2);
const parentDirName = path.dirname(contentItem.slug);
if (headersH2.length !== 0) {
const entryInPageNavData = convertHeadingsToInPageNavData(headersH2, urlPath)[0];
const headersH3 = contentItem.headings.filter(header => header.depth === 3);
if (headersH3.length !== 0) {
entryInPageNavData.children = convertHeadingsToInPageNavData(headersH3, urlPath);
}
inPageNavData.push(entryInPageNavData);
parentDirToNavDataMap.set(parentDirName, entryInPageNavData);
} else {
const headersH3 = contentItem.headings.filter(header => header.depth === 3);
if (headersH3.length !== 0) {
const entryInPageNavData = parentDirToNavDataMap.get(parentDirName);
if (entryInPageNavData) {
entryInPageNavData.children = entryInPageNavData.children || [];
entryInPageNavData.children = [...entryInPageNavData.children, ...convertHeadingsToInPageNavData(headersH3, urlPath)];
}
}
}
}
return inPageNavData;
};
function getContentsWithParentDepth(contents, parentDepth) {
return contents.filter(content => {
const dirDepth = path.dirname(content.slug).split('/').length;
return dirDepth >= parentDepth;
});
}
function getSlugForParentDepth(slug, depth) {
const slugParts = slug.split('/');
const slugPartsForParentDepth = slugParts.slice(0, depth);
return slugPartsForParentDepth.join('/');
}
function getUniqueParentDirs(contents, parentDepth) {
const dirs = new Set();
contents.forEach(content => {
dirs.add(getSlugForParentDepth(content.slug, parentDepth));
});
return [...dirs];
}
function sortDirs(dirs, contents) {
dirs.sort((a, b) => {
const aDirOrder = contents.find(content => content.slug === path.join(a, 'dir-index')).order;
const bDirOrder = contents.find(content => content.slug === path.join(b, 'dir-index')).order;
return aDirOrder < bDirOrder ? -1 : 1;
});
}
function sortDirectoriesForParentDepth(contents, parentDepth) {
const reducedContents = getContentsWithParentDepth(contents, parentDepth);
const uniqueParentDirs = getUniqueParentDirs(reducedContents, parentDepth);
sortDirs(uniqueParentDirs, reducedContents);
contents.sort((a, b) => {
const aParentDir = getSlugForParentDepth(a.slug, parentDepth);
const bParentDir = getSlugForParentDepth(b.slug, parentDepth);
if (uniqueParentDirs.indexOf(aParentDir) > uniqueParentDirs.indexOf(bParentDir)) {
return 1;
} else if (uniqueParentDirs.indexOf(aParentDir) < uniqueParentDirs.indexOf(bParentDir)) {
return -1;
} else {
return 0;
}
});
}
function sortDirectories(contents) {
let parentDepth = maxDepthForNonComponentsNavigation + 1;
let hasParentWithDepth = contents.some(content => path.dirname(content.slug).split('/').length === parentDepth);
while(hasParentWithDepth) {
sortDirectoriesForParentDepth(contents, parentDepth);
parentDepth++;
hasParentWithDepth = contents.some(content => path.dirname(content.slug).split('/').length === parentDepth);
}
}
function sort(contents) {
contents.sort((a, b) => {
// Get paths with fewer depth first
if (a.slug.split('/').length < b.slug.split('/').length) {
return -1;
} else if (a.slug.split('/').length > b.slug.split('/').length) {
return 1;
}
// same depth
else {
// same parent
if (path.dirname(a.slug) === path.dirname(b.slug)) {
if (path.basename(a.slug) === 'dir-index') {
return -1;
} else if (path.basename(b.slug) === 'dir-index') {
return 1;
} else {
return a.order < b.order ? -1 : 1;
}
}
}
});
}
export async function getPages(entries) {
const contents = await parseEntries(entries);
sortDirectories(contents);
sort(contents);
return contents;
}
export const getPagesByDir = async (directoryPath) => {
const entries = getEntriesByDir(directoryPath);
return await getPages(entries);
};
const getEntriesByDir = (dirname) => {
return allPages.filter(childEntry => {
return childEntry.slug.startsWith(dirname)});
};
const getMdjsStories = (fullDirPath) => {
return new Promise((resolve, reject) => {
glob(fullDirPath + '/**/__mdjs-stories--*.js', {}, (err, files)=>{
const relativePaths = files.map(file => {
return path.relative(fullDirPath, file);
});
resolve(relativePaths);
})
});
};
/**
* @param {boolean} isUrlPathADirectory `True` if the `urlPath` paramter is a url path to a directory with multiple md files.
* `false` if the `urlPath` paramter is a url path to a single md file
*/
export async function getPathForMdjsStroriesFile(isUrlPathADirectory, urlPath) {
if (!isUrlPathADirectory) {
const mdjsStroriesFileDirectory = path.dirname(urlPath);
return `/docs/${mdjsStroriesFileDirectory}/__mdjs-stories--${path.basename(urlPath)}.js`;
}
if (urlPath) {
let mdjsStoriesJsPath = '';
const fullDirPath = path.join(process.cwd(), 'public/docs', urlPath);
const files = await getMdjsStories(fullDirPath);
let imports = '';
files.forEach(file => {
imports += `import('./${file}');\n`
});
if (imports) {
mdjsStoriesJsPath = path.join(fullDirPath, '__mdjs-stories.js');
fs.writeFileSync(mdjsStoriesJsPath, imports, 'utf8');
}
if (mdjsStoriesJsPath) {
return '/docs' + mdjsStoriesJsPath.split('/docs')[1];
}
}
return null;
}

View file

@ -0,0 +1,5 @@
const { postBuildDist } = require('./post-build-dist.js');
module.exports = {
postBuildDist,
};

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1,32 @@
const { execSync } = require('child_process');
const fs = require('fs');
const distDocs = 'dist/docs';
const getAllFiles = (dirPath, arrayOfFiles) => {
const files = fs.readdirSync(dirPath);
// eslint-disable-next-line no-param-reassign
arrayOfFiles = arrayOfFiles || [];
files.forEach(file => {
if (fs.statSync(`${dirPath}/${file}`).isDirectory()) {
// eslint-disable-next-line no-param-reassign
arrayOfFiles = getAllFiles(`${dirPath}/${file}`, arrayOfFiles);
} else if (file === '__mdjs-stories.js') {
execSync(
`npx rollup ${dirPath}/__mdjs-stories.js --config rollup.config-test.js --dir ${dirPath}/`,
);
}
});
return arrayOfFiles;
};
const postBuildDist = () => {
getAllFiles(distDocs);
};
module.exports = {
postBuildDist,
};

View file

@ -0,0 +1,88 @@
// eslint-disable-next-line import/no-extraneous-dependencies
const { init } = require('es-module-lexer');
const path = require('path');
let isToBeConcatenated;
let maxDepthForNonComponentsNavigation;
let docsDirName;
let visit;
(async () => {
// eslint-disable-next-line import/no-extraneous-dependencies
const result = await import('unist-util-visit');
visit = result.visit;
const config = await import('../../../../config.mjs');
isToBeConcatenated = config.isToBeConcatenated;
maxDepthForNonComponentsNavigation = config.maxDepthForNonComponentsNavigation;
docsDirName = config.docsDirName;
})();
// function addOverviewTitleToIndexMd(tree, isIndexMd) {
// let h1Index = null;
// tree.children.forEach((item, index) => {
// if (item.depth === 1 && item.type === 'heading' && isIndexMd) {
// h1Index = index;
// }
// });
// if (h1Index !== null) {
// tree.children.splice(1, 0, {
// type: 'heading',
// depth: 2,
// children: [
// {
// type: 'text',
// value: 'Index',
// },
// ],
// });
// }
// }
function cleanupRocketMetadata() {
/**
* @param {Node} tree
*/
async function transformer(tree, file) {
const filePath = file.history[0];
const isIndexMd = path.basename(filePath) === 'dir-index.md';
const filePathFromProjectRoot = filePath.split(docsDirName)[1];
const depthDelta =
path.dirname(filePathFromProjectRoot).split('/').length - maxDepthForNonComponentsNavigation;
/**
* @param {UnistNode} _node
*/
async function nodeCodeVisitor(_node, index, parent) {
if (parent.type === 'heading' && isToBeConcatenated(filePath)) {
if (parent.depth === 1) {
const splitByOrder = _node.value.split('||');
const splitByArrows = splitByOrder[0].split('>>');
const title = splitByArrows[splitByArrows.length - 1].trim();
// eslint-disable-next-line no-param-reassign
_node.value = title;
if (isIndexMd) {
if (depthDelta > 0) {
// eslint-disable-next-line no-param-reassign
parent.depth += depthDelta;
}
return;
}
}
// eslint-disable-next-line no-param-reassign
parent.depth += 1 + depthDelta;
}
}
// unifiedjs expects node changes to be made on the given node...
await init;
visit(tree, ['text', 'inlineCode'], nodeCodeVisitor);
// addOverviewTitleToIndexMd(tree, isIndexMd);
return tree;
}
return transformer;
}
module.exports = {
cleanupRocketMetadata,
};

View file

@ -0,0 +1,5 @@
const { cleanupRocketMetadata } = require('./cleanupRocketMetadata.js');
module.exports = {
cleanupRocketMetadata,
};

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1,147 @@
const path = require('path');
const fs = require('fs');
// eslint-disable-next-line import/no-extraneous-dependencies
const { init, parse } = require('es-module-lexer');
let visit;
(async () => {
// eslint-disable-next-line import/no-extraneous-dependencies
const result = await import('unist-util-visit');
visit = result.visit;
})();
const nodeModulesText = '/node_modules';
const mdJsStoriesFileNameWithoutExtension = '__mdjs-stories';
const mdJsStoriesFileName = `${mdJsStoriesFileNameWithoutExtension}.js`;
const isDistBuild = process.env.PROD === 'true';
/**
* @param {string} source
* @param {string} inputPath
*/
async function processImports(source) {
if (source !== '' && source.includes('import')) {
let newSource = '';
let lastPos = 0;
await init;
const [imports] = parse(source);
for (const importObj of imports) {
newSource += source.substring(lastPos, importObj.s);
const importSrc = source.substring(importObj.s, importObj.e);
const isDynamicImport = importObj.d > -1;
if (
importSrc.startsWith('.') ||
importSrc.startsWith('/') ||
isDynamicImport ||
importSrc.startsWith('import.')
) {
if (importSrc === `'@mdjs/mdjs-preview/define'`) {
newSource += `'${nodeModulesText}/@mdjs/mdjs-preview/src/define/define.js'`;
} else if (importSrc === `'@mdjs/mdjs-story/define'`) {
newSource += `'${nodeModulesText}/@mdjs/mdjs-story/src/define.js'`;
} else {
newSource += importSrc;
}
} else {
const resolvedPath = require.resolve(importSrc);
const packagesPath = '/packages/';
if (resolvedPath.includes(packagesPath)) {
newSource += packagesPath + resolvedPath.split(packagesPath)[1];
} else {
newSource += nodeModulesText + require.resolve(importSrc).split(nodeModulesText)[1];
}
}
lastPos = importObj.e;
}
newSource += source.substring(lastPos, source.length);
return newSource;
}
return source;
}
function copyMdjsStories() {
/**
* @param {Node} tree
* @param {VFileOptions} file
*/
async function transformer(tree, file) {
let pathToMdDirectoryInPublic = '';
let currentMarkdownFile = '';
let currentMarkdownFileMdJsStoryName = '';
/**
* @param {UnistNode} _node
*/
async function nodeCodeVisitor(_node, index, parent) {
if (
parent.type === 'heading' &&
parent.depth === 1 &&
currentMarkdownFile.includes('/components')
) {
const commonMdjsStoriesFileName = `${pathToMdDirectoryInPublic}/${mdJsStoriesFileName}`;
let commonMdjsStoriesContent = '';
try {
commonMdjsStoriesContent = fs.readFileSync(commonMdjsStoriesFileName).toString();
} catch (ex) {
// noop. File is not yet created for the component
}
const exportCmd = `import('./${currentMarkdownFileMdJsStoryName}');\n`;
if (commonMdjsStoriesContent.indexOf(exportCmd) === -1) {
fs.writeFileSync(commonMdjsStoriesFileName, commonMdjsStoriesContent + exportCmd, 'utf8');
}
}
}
let { setupJsCode } = file.data;
if (!setupJsCode) {
return tree;
}
// eslint-disable-next-line prefer-destructuring
currentMarkdownFile = file.history[0];
const { cwd } = file;
const publicDir = `${cwd}/public`;
let parsedPath = '';
if (currentMarkdownFile) {
const leftSideParsedPath = currentMarkdownFile.split('src/content/')[1];
// eslint-disable-next-line prefer-destructuring
parsedPath = path.dirname(leftSideParsedPath);
}
let parsedSetupJsCode;
// This is copied from @mdjs/core/src/mdjsSetupCode.js
const initialImprorts = `
import '@mdjs/mdjs-preview/define';
import '@mdjs/mdjs-story/define'; \n`;
setupJsCode = initialImprorts + setupJsCode;
if (isDistBuild) {
parsedSetupJsCode += await setupJsCode;
} else {
parsedSetupJsCode += await processImports(setupJsCode);
}
pathToMdDirectoryInPublic = `${publicDir}/${parsedPath}`;
currentMarkdownFileMdJsStoryName = `${mdJsStoriesFileNameWithoutExtension}--${
path.basename(currentMarkdownFile).split('.md')[0]
}.js`;
const newName = path.join(pathToMdDirectoryInPublic, currentMarkdownFileMdJsStoryName);
await fs.promises.mkdir(pathToMdDirectoryInPublic, { recursive: true });
await fs.promises.writeFile(newName, parsedSetupJsCode, 'utf8');
// unifiedjs expects node changes to be made on the given node...
await init;
visit(tree, 'text', nodeCodeVisitor);
return tree;
}
return transformer;
}
module.exports = {
copyMdjsStories,
};

View file

@ -0,0 +1,5 @@
const { copyMdjsStories } = require('./copyMdjsStories.js');
module.exports = {
copyMdjsStories,
};

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1,5 @@
const { updateMainTagsForMdjsStories } = require('./updateMainTagsForMdjsStories.js');
module.exports = {
updateMainTagsForMdjsStories,
};

View file

@ -0,0 +1,67 @@
// eslint-disable-next-line import/no-extraneous-dependencies
const { init, parse } = require('es-module-lexer');
const path = require('path');
let visit;
(async () => {
// eslint-disable-next-line import/no-extraneous-dependencies
const result = await import('unist-util-visit');
visit = result.visit;
})();
/**
* @param {string} code
* @param {{type: StoryTypes}} options
* @returns {Story}
*/
function extractStoryData(code, { type = 'js' } = { type: 'js' }) {
const parsed = parse(code);
const key = parsed[1][0];
const name = key;
return { key, name, code, type };
}
function updateMainTagsForMdjsStories() {
let parsedPath = '';
/**
* @param {UnistNode} _node
*/
const nodeCodeVisitor = _node => {
const node = /** @type {UnistNode & {[key: string]: unknown}} */ (_node);
if (node.lang === 'js' && node.meta === 'preview-story' && typeof node.value === 'string') {
const storyData = extractStoryData(node.value);
const mainTagName = storyData.name;
let mdFileName = path.basename(parsedPath);
mdFileName = mdFileName.replaceAll('-', '_');
node.value = node.value.replace(mainTagName, `${mainTagName}__${mdFileName}`);
}
};
/**
* @param {Node} tree
* @param {VFileOptions} file
*/
async function transformer(tree, file) {
const currentMarkdownFile = file.history[0];
if (currentMarkdownFile) {
const leftSideParsedPath = currentMarkdownFile.split('src/content/')[1];
// eslint-disable-next-line prefer-destructuring
parsedPath = leftSideParsedPath.split('.md')[0];
}
// unifiedjs expects node changes to be made on the given node...
await init;
// @ts-ignore
visit(tree, 'code', nodeCodeVisitor);
return tree;
}
return transformer;
}
module.exports = {
updateMainTagsForMdjsStories,
};

View file

@ -1,23 +1,3 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "NodeNext",
"lib": ["es2017", "dom"],
"allowJs": true,
"checkJs": true,
"strict": true,
"noImplicitThis": true,
"composite": true,
"emitDeclarationOnly": true,
"alwaysStrict": true,
"types": ["node", "mocha", "chai", "sinon"],
"esModuleInterop": true,
"suppressImplicitAnyIndexErrors": true,
"skipLibCheck": true,
"incremental": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
}
"extends": "astro/tsconfigs/base"
}