mirror of
https://github.com/modernweb-dev/rocket.git
synced 2026-03-21 08:51:18 +00:00
Compare commits
28 Commits
@rocket/cl
...
feat/playg
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00ebeea10f | ||
|
|
818caad7cb | ||
|
|
672b7e893e | ||
|
|
a8e66d84f4 | ||
|
|
e9090d64b9 | ||
|
|
728a205b7b | ||
|
|
67ba29d45a | ||
|
|
758caffdf9 | ||
|
|
302227e8a9 | ||
|
|
04a31220fb | ||
|
|
d44a23af0c | ||
|
|
18a79589c2 | ||
|
|
b7727b0e10 | ||
|
|
5edc40fed5 | ||
|
|
be0d0b3ca1 | ||
|
|
ef8ebb0098 | ||
|
|
2fa61e1377 | ||
|
|
b23e37f38e | ||
|
|
cf45e32702 | ||
|
|
b5965c6c37 | ||
|
|
e39cc45d23 | ||
|
|
f0434cb12c | ||
|
|
c87caaed2d | ||
|
|
04af7ecf53 | ||
|
|
98d6aad12a | ||
|
|
ee6b404aaa | ||
|
|
8ba8939c67 | ||
|
|
8e095b792e |
5
.changeset/chilled-turkeys-help.md
Normal file
5
.changeset/chilled-turkeys-help.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@rocket/search': patch
|
||||
---
|
||||
|
||||
chore: generalize label & add alt when no img
|
||||
5
.changeset/many-points-eat.md
Normal file
5
.changeset/many-points-eat.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@mdjs/core': minor
|
||||
---
|
||||
|
||||
Extract building of the JavaScript setup code into a unified plugin called mdjsSetupCode
|
||||
5
.changeset/yellow-moose-play.md
Normal file
5
.changeset/yellow-moose-play.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@mdjs/core': patch
|
||||
---
|
||||
|
||||
You can provide a highlightCode function to the mdjsSetupCode unified plugin
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
## The Goal for Rocket
|
||||
|
||||
> Our goal is to provide developers with a meta framework for static websites with a spricle of JavaScript.
|
||||
> Our goal is to provide developers with a meta framework for static websites with a sprinkle of JavaScript.
|
||||
|
||||
Get a site up and running in no time and focus on the content.
|
||||
You can still tweak every detail of every underlying tool that gets used.
|
||||
@@ -54,7 +54,7 @@ Rocket is part of the [Modern Web Family](https://twitter.com/modern_web_dev).
|
||||
|
||||
We are always looking for contributors of all skill levels! If you're looking to ease your way into the project, try out a [good first issue](https://github.com/modernweb-dev/rocket/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).
|
||||
|
||||
If you are interested in helping contribute to Modern Web, please take a look at our [Contributing Guide](https://github.com/modernweb-dev/rocket/blob/main/CONTRIBUTING.md). Also, feel free to drop into [slack](https://rocket.modern-web.dev/discover/slack/) and say hi. 👋
|
||||
If you are interested in helping contribute to Modern Web, please take a look at our [Contributing Guide](https://github.com/modernweb-dev/rocket/blob/main/CONTRIBUTING.md). Also, feel free to drop into [slack](https://rocket.modern-web.dev/about/slack/) and say hi. 👋
|
||||
|
||||
### Financial Contributors
|
||||
|
||||
|
||||
14
docs/_includes/layout-playground.njk
Normal file
14
docs/_includes/layout-playground.njk
Normal file
@@ -0,0 +1,14 @@
|
||||
<html lang="en">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Playground</title>
|
||||
<body>
|
||||
<rocket-playground></rocket-playground>
|
||||
|
||||
<script src="https://unpkg.com/wasm-flate@0.1.12-alpha/dist/bootstrap.js"></script>
|
||||
|
||||
<script type="module">
|
||||
import '@rocket/playground/rocket-playground';
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -132,31 +132,6 @@ export const header = () => {
|
||||
|
||||
## Supported Systems
|
||||
|
||||
### es-dev-server
|
||||
|
||||
Preview your mdjs readme with live demos and auto reload.
|
||||
|
||||
- Add to your `package.json`:
|
||||
|
||||
```json
|
||||
"scripts": {
|
||||
"start": "es-dev-server",
|
||||
}
|
||||
```
|
||||
|
||||
- Create a `es-dev-server.config.js` in the root of your repository.
|
||||
|
||||
```js
|
||||
const { mdjsTransformer } = require('@mdjs/core');
|
||||
|
||||
module.exports = {
|
||||
nodeResolve: true,
|
||||
open: 'README.md',
|
||||
watch: true,
|
||||
responseTransformers: [mdjsTransformer],
|
||||
};
|
||||
```
|
||||
|
||||
### Storybook
|
||||
|
||||
Please check out [@open-wc/demoing-storybook](https://open-wc.org/demoing/) for a fully integrated setup.
|
||||
|
||||
@@ -2,10 +2,16 @@
|
||||
|
||||
You can showcase live running code by annotating a code block with `js preview-story`.
|
||||
|
||||
````md
|
||||
```js preview-story
|
||||
```js script
|
||||
import { html } from 'lit-html';
|
||||
```
|
||||
|
||||
````md
|
||||
```js script
|
||||
import { html } from 'lit-html';
|
||||
```
|
||||
|
||||
```js preview-story
|
||||
export const foo = () => html` <p>my html</p> `;
|
||||
```
|
||||
````
|
||||
@@ -13,7 +19,5 @@ export const foo = () => html` <p>my html</p> `;
|
||||
will result in
|
||||
|
||||
```js preview-story
|
||||
import { html } from 'lit-html';
|
||||
|
||||
export const foo = () => html` <p>my html</p> `;
|
||||
```
|
||||
|
||||
@@ -2,10 +2,16 @@
|
||||
|
||||
You can showcase live running code by annotating a code block with `js story`.
|
||||
|
||||
````md
|
||||
```js story
|
||||
```js script
|
||||
import { html } from 'lit-html';
|
||||
```
|
||||
|
||||
````md
|
||||
```js script
|
||||
import { html } from 'lit-html';
|
||||
```
|
||||
|
||||
```js story
|
||||
export const foo = () => html` <p>my html</p> `;
|
||||
```
|
||||
````
|
||||
@@ -13,7 +19,5 @@ export const foo = () => html` <p>my html</p> `;
|
||||
will result in
|
||||
|
||||
```js story
|
||||
import { html } from 'lit-html';
|
||||
|
||||
export const foo = () => html` <p>my html</p> `;
|
||||
```
|
||||
|
||||
@@ -47,3 +47,9 @@ export const headlineConverter = () => html`
|
||||
```
|
||||
|
||||
How it then works is very similar to https://www.11ty.dev/docs/plugins/navigation/
|
||||
|
||||
## Sidebar redirects
|
||||
|
||||
By default, the sidebar nav redirects clicks on category headings to the first child page in that category.
|
||||
|
||||
To disable those redirects, override `_includes/_joiningBlocks/_layoutSidebar/sidebar/20-navigation.njk` and add the `no-redirects` attribute to the `<rocket-navigation>` element.
|
||||
|
||||
3
docs/playground.md
Normal file
3
docs/playground.md
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
layout: layout-playground
|
||||
---
|
||||
@@ -38,6 +38,6 @@
|
||||
"testing"
|
||||
],
|
||||
"dependencies": {
|
||||
"plugins-manager": "^0.2.0"
|
||||
"plugins-manager": "^0.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @rocket/building-rollup
|
||||
|
||||
## 0.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- be0d0b3: fix: add missing main entry to the packages
|
||||
|
||||
## 0.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@rocket/building-rollup",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
@@ -14,6 +14,7 @@
|
||||
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
|
||||
"homepage": "https://rocket.modern-web.dev/docs/tools/building-rollup/",
|
||||
"type": "module",
|
||||
"main": "./index.js",
|
||||
"exports": {
|
||||
".": "./index.js"
|
||||
},
|
||||
|
||||
@@ -53,7 +53,7 @@ export function createSpaMetaConfig(userConfig = { output: {} }) {
|
||||
// directory to match patterns against to be precached
|
||||
globDirectory: path.join(config.output.dir),
|
||||
// cache any html js and css by default
|
||||
globPatterns: ['**/*.{html,js,css,webmanifest}'],
|
||||
globPatterns: ['**/*.{html,js,css,webmanifest}', '**/*-search-index.json'],
|
||||
skipWaiting: true,
|
||||
clientsClaim: true,
|
||||
runtimeCaching: [
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# check-html-links
|
||||
|
||||
## 0.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- be0d0b3: fix: add missing main entry to the packages
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "check-html-links",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
@@ -17,6 +17,7 @@
|
||||
"check-html-links": "src/cli.js"
|
||||
},
|
||||
"type": "module",
|
||||
"main": "./index.js",
|
||||
"exports": {
|
||||
".": "./index.js"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,33 @@
|
||||
# @rocket/cli
|
||||
|
||||
## 0.6.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 5edc40f: Make sure each rocket instance has it's own eleventy config'
|
||||
- ef8ebb0: To support dynamically created content to be part of the anchor navigation of the page we now analyze the final html output instead of `entry.templateContent`.
|
||||
|
||||
BREAKING CHANGE:
|
||||
|
||||
- only add anchors for the currently active pages (before it added anchor for every page)
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- be0d0b3: fix: add missing main entry to the packages
|
||||
- Updated dependencies [be0d0b3]
|
||||
- Updated dependencies [ef8ebb0]
|
||||
- @rocket/building-rollup@0.1.3
|
||||
- check-html-links@0.2.1
|
||||
- @rocket/core@0.1.2
|
||||
- plugins-manager@0.2.1
|
||||
- @rocket/eleventy-rocket-nav@0.3.0
|
||||
|
||||
## 0.5.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 8e095b7: Watching `_assets`, `_data`, `_includes` for changes to trigger updated automatically
|
||||
|
||||
## 0.5.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@rocket/cli",
|
||||
"version": "0.5.1",
|
||||
"version": "0.6.0",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
@@ -17,6 +17,7 @@
|
||||
"rocket": "src/cli.js"
|
||||
},
|
||||
"type": "module",
|
||||
"main": "./index.cjs",
|
||||
"exports": {
|
||||
".": {
|
||||
"require": "./index.cjs",
|
||||
@@ -56,22 +57,22 @@
|
||||
"dependencies": {
|
||||
"@11ty/eleventy": "^0.11.1",
|
||||
"@11ty/eleventy-img": "^0.7.4",
|
||||
"@rocket/building-rollup": "^0.1.2",
|
||||
"@rocket/core": "^0.1.1",
|
||||
"@rocket/building-rollup": "^0.1.3",
|
||||
"@rocket/core": "^0.1.2",
|
||||
"@rocket/eleventy-plugin-mdjs-unified": "^0.3.1",
|
||||
"@rocket/eleventy-rocket-nav": "^0.2.1",
|
||||
"@rocket/eleventy-rocket-nav": "^0.3.0",
|
||||
"@rollup/plugin-babel": "^5.2.2",
|
||||
"@rollup/plugin-node-resolve": "^11.0.1",
|
||||
"@web/config-loader": "^0.1.3",
|
||||
"@web/dev-server": "^0.1.4",
|
||||
"@web/dev-server-rollup": "^0.3.2",
|
||||
"@web/rollup-plugin-copy": "^0.2.0",
|
||||
"check-html-links": "^0.2.0",
|
||||
"check-html-links": "^0.2.1",
|
||||
"command-line-args": "^5.1.1",
|
||||
"command-line-usage": "^6.1.1",
|
||||
"fs-extra": "^9.0.1",
|
||||
"micromatch": "^4.0.2",
|
||||
"plugins-manager": "^0.2.0",
|
||||
"plugins-manager": "^0.2.1",
|
||||
"utf8": "^3.0.0"
|
||||
},
|
||||
"types": "dist-types/index.d.ts"
|
||||
|
||||
@@ -10,6 +10,7 @@ import computedConfigPkg from './public/computedConfig.cjs';
|
||||
|
||||
import path from 'path';
|
||||
import Eleventy from '@11ty/eleventy';
|
||||
import TemplateConfig from '@11ty/eleventy/src/TemplateConfig.js';
|
||||
import { fileURLToPath } from 'url';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
@@ -33,6 +34,35 @@ export class RocketEleventy extends Eleventy {
|
||||
await super.write();
|
||||
await this.__rocketCli.update();
|
||||
}
|
||||
|
||||
// forks it so we can watch for changes but don't include them while building
|
||||
getChokidarConfig() {
|
||||
let ignores = this.eleventyFiles.getGlobWatcherIgnores();
|
||||
|
||||
const keepWatching = [
|
||||
path.join(this.__rocketCli.config._inputDirCwdRelative, '_assets', '**'),
|
||||
path.join(this.__rocketCli.config._inputDirCwdRelative, '_data', '**'),
|
||||
path.join(this.__rocketCli.config._inputDirCwdRelative, '_includes', '**'),
|
||||
];
|
||||
|
||||
ignores = ignores.filter(ignore => !keepWatching.includes(ignore));
|
||||
// debug("Ignoring watcher changes to: %o", ignores);
|
||||
|
||||
let configOptions = this.config.chokidarConfig;
|
||||
|
||||
// can’t override these yet
|
||||
// TODO maybe if array, merge the array?
|
||||
delete configOptions.ignored;
|
||||
|
||||
return Object.assign(
|
||||
{
|
||||
ignored: ignores,
|
||||
ignoreInitial: true,
|
||||
// also interesting: awaitWriteFinish
|
||||
},
|
||||
configOptions,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class RocketCli {
|
||||
@@ -70,11 +100,16 @@ export class RocketCli {
|
||||
await this.mergePresets();
|
||||
|
||||
const elev = new RocketEleventy(_inputDirCwdRelative, outputDevDir, this);
|
||||
elev.isVerbose = false;
|
||||
// 11ty always wants a relative path to cwd - why?
|
||||
const rel = path.relative(process.cwd(), path.join(__dirname));
|
||||
const relCwdPathToConfig = path.join(rel, 'shared', '.eleventy.cjs');
|
||||
|
||||
const config = new TemplateConfig(null, relCwdPathToConfig);
|
||||
elev.config = config.getConfig();
|
||||
elev.resetConfig();
|
||||
elev.setConfigPathOverride(relCwdPathToConfig);
|
||||
|
||||
elev.isVerbose = false;
|
||||
await elev.init();
|
||||
|
||||
this.eleventy = elev;
|
||||
|
||||
@@ -58,6 +58,12 @@ export class RocketStart {
|
||||
|
||||
setupRollupPlugins: this.config.setupDevAndBuildPlugins,
|
||||
setupPlugins: this.config.setupDevPlugins,
|
||||
middleware: [
|
||||
function rewriteIndex(context, next) {
|
||||
context.set('Access-Control-Allow-Origin', '*');
|
||||
return next();
|
||||
},
|
||||
],
|
||||
},
|
||||
[],
|
||||
{ rollupWrapperFunction: fromRollup },
|
||||
|
||||
@@ -55,6 +55,10 @@ export async function readOutput(
|
||||
type = 'build',
|
||||
} = {},
|
||||
) {
|
||||
if (!cli || !cli.config) {
|
||||
throw new Error(`No valid cli provided to readOutput - you passed a ${typeof cli}: ${cli}`);
|
||||
}
|
||||
|
||||
const outputDir = type === 'build' ? cli.config.outputDir : cli.config.outputDevDir;
|
||||
let text = await fs.promises.readFile(path.join(outputDir, fileName));
|
||||
text = text.toString();
|
||||
@@ -116,6 +120,7 @@ export async function execute(cli, configFileDir) {
|
||||
await cli.setup();
|
||||
cli.config.outputDevDir = path.join(configFileDir, '__output-dev');
|
||||
cli.config.devServer.open = false;
|
||||
cli.config.devServer.port = 8080;
|
||||
cli.config.watch = false;
|
||||
cli.config.outputDir = path.join(configFileDir, '__output');
|
||||
await cli.run();
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
executeBuild,
|
||||
executeStart,
|
||||
readBuildOutput,
|
||||
readOutput,
|
||||
readStartOutput,
|
||||
setFixtureDir,
|
||||
} from '@rocket/cli/test-helpers';
|
||||
@@ -29,37 +28,27 @@ describe('RocketCli computedConfig', () => {
|
||||
it('will extract a title from markdown and set first folder as section', async () => {
|
||||
cli = await executeStart('computed-config-fixtures/headlines/rocket.config.js');
|
||||
|
||||
const indexHtml = await readOutput(cli, 'index.html', {
|
||||
type: 'start',
|
||||
});
|
||||
const indexHtml = await readStartOutput(cli, 'index.html');
|
||||
const [indexTitle, indexSection] = indexHtml.split('\n');
|
||||
expect(indexTitle).to.equal('Root');
|
||||
expect(indexSection).to.be.undefined;
|
||||
|
||||
const subHtml = await readOutput(cli, 'sub/index.html', {
|
||||
type: 'start',
|
||||
});
|
||||
const subHtml = await readStartOutput(cli, 'sub/index.html');
|
||||
const [subTitle, subSection] = subHtml.split('\n');
|
||||
expect(subTitle).to.equal('Root: Sub');
|
||||
expect(subSection).to.equal('sub');
|
||||
|
||||
const subSubHtml = await readOutput(cli, 'sub/subsub/index.html', {
|
||||
type: 'start',
|
||||
});
|
||||
const subSubHtml = await readStartOutput(cli, 'sub/subsub/index.html');
|
||||
const [subSubTitle, subSubSection] = subSubHtml.split('\n');
|
||||
expect(subSubTitle).to.equal('Sub: SubSub');
|
||||
expect(subSubSection).to.equal('sub');
|
||||
|
||||
const sub2Html = await readOutput(cli, 'sub2/index.html', {
|
||||
type: 'start',
|
||||
});
|
||||
const sub2Html = await readStartOutput(cli, 'sub2/index.html');
|
||||
const [sub2Title, sub2Section] = sub2Html.split('\n');
|
||||
expect(sub2Title).to.equal('Root: Sub2');
|
||||
expect(sub2Section).to.equal('sub2');
|
||||
|
||||
const withDataHtml = await readOutput(cli, 'with-data/index.html', {
|
||||
type: 'start',
|
||||
});
|
||||
const withDataHtml = await readStartOutput(cli, 'with-data/index.html');
|
||||
const [withDataTitle, withDataSection] = withDataHtml.split('\n');
|
||||
expect(withDataTitle).to.equal('Set via data');
|
||||
expect(withDataSection).be.undefined;
|
||||
@@ -199,9 +188,7 @@ describe('RocketCli computedConfig', () => {
|
||||
it('can be configured via setupEleventyComputedConfig', async () => {
|
||||
cli = await executeStart('computed-config-fixtures/setup/addPlugin.rocket.config.js');
|
||||
|
||||
const indexHtml = await readOutput(cli, 'index.html', {
|
||||
type: 'start',
|
||||
});
|
||||
const indexHtml = await readStartOutput(cli, 'index.html');
|
||||
expect(indexHtml).to.equal('test-value');
|
||||
});
|
||||
|
||||
|
||||
@@ -1,86 +1,25 @@
|
||||
import chai from 'chai';
|
||||
import fetch from 'node-fetch';
|
||||
import { RocketCli } from '../src/RocketCli.js';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import fs from 'fs-extra';
|
||||
import chalk from 'chalk';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
import {
|
||||
executeBuild,
|
||||
executeLint,
|
||||
executeStart,
|
||||
expectThrowsAsync,
|
||||
readBuildOutput,
|
||||
readStartOutput,
|
||||
setFixtureDir,
|
||||
} from '@rocket/cli/test-helpers';
|
||||
|
||||
const { expect } = chai;
|
||||
|
||||
/**
|
||||
* @param {function} method
|
||||
* @param {string} errorMessage
|
||||
*/
|
||||
async function expectThrowsAsync(method, { errorMatch, errorMessage } = {}) {
|
||||
let error = null;
|
||||
try {
|
||||
await method();
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
expect(error).to.be.an('Error', 'No error was thrown');
|
||||
if (errorMatch) {
|
||||
expect(error.message).to.match(errorMatch);
|
||||
}
|
||||
if (errorMessage) {
|
||||
expect(error.message).to.equal(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
describe('RocketCli e2e', () => {
|
||||
let cli;
|
||||
|
||||
async function readOutput(
|
||||
fileName,
|
||||
{
|
||||
stripServiceWorker = false,
|
||||
stripToBody = false,
|
||||
stripStartEndWhitespace = true,
|
||||
type = 'build',
|
||||
} = {},
|
||||
) {
|
||||
const outputDir = type === 'build' ? cli.config.outputDir : cli.config.outputDevDir;
|
||||
let text = await fs.promises.readFile(path.join(outputDir, fileName));
|
||||
text = text.toString();
|
||||
if (stripToBody) {
|
||||
const bodyOpenTagEnd = text.indexOf('>', text.indexOf('<body') + 1) + 1;
|
||||
const bodyCloseTagStart = text.indexOf('</body>');
|
||||
text = text.substring(bodyOpenTagEnd, bodyCloseTagStart);
|
||||
}
|
||||
if (stripServiceWorker) {
|
||||
const scriptOpenTagEnd = text.indexOf('<script inject-service-worker');
|
||||
const scriptCloseTagStart = text.indexOf('</script>', scriptOpenTagEnd) + 9;
|
||||
text = text.substring(0, scriptOpenTagEnd) + text.substring(scriptCloseTagStart);
|
||||
}
|
||||
if (stripStartEndWhitespace) {
|
||||
text = text.trim();
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
async function execute() {
|
||||
await cli.setup();
|
||||
cli.config.outputDevDir = path.join(__dirname, 'e2e-fixtures', '__output-dev');
|
||||
cli.config.devServer.open = false;
|
||||
cli.config.devServer.port = 8080;
|
||||
cli.config.watch = false;
|
||||
cli.config.outputDir = path.join(__dirname, 'e2e-fixtures', '__output');
|
||||
await cli.run();
|
||||
}
|
||||
|
||||
async function executeLint(pathToConfig) {
|
||||
cli = new RocketCli({
|
||||
argv: ['lint', '--config-file', path.join(__dirname, pathToConfig.split('/').join(path.sep))],
|
||||
});
|
||||
await execute();
|
||||
}
|
||||
|
||||
before(() => {
|
||||
// ignore colors in tests as most CIs won't support it
|
||||
chalk.level = 0;
|
||||
setFixtureDir(import.meta.url);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -90,79 +29,39 @@ describe('RocketCli e2e', () => {
|
||||
});
|
||||
|
||||
it('can add a unified plugin via the config', async () => {
|
||||
cli = new RocketCli({
|
||||
argv: [
|
||||
'build',
|
||||
'--config-file',
|
||||
path.join(__dirname, 'e2e-fixtures', 'unified-plugin', 'rocket.config.js'),
|
||||
],
|
||||
});
|
||||
await execute();
|
||||
const indexHtml = await readOutput('index.html', {
|
||||
stripServiceWorker: true,
|
||||
stripToBody: true,
|
||||
});
|
||||
|
||||
cli = await executeStart('e2e-fixtures/unified-plugin/rocket.config.js');
|
||||
const indexHtml = await readStartOutput(cli, 'index.html');
|
||||
expect(indexHtml).to.equal('<p>See a 🐶</p>');
|
||||
});
|
||||
|
||||
describe('eleventy in config', () => {
|
||||
// TODO: find out while this has a side effect and breaks other tests
|
||||
it.skip('can modify eleventy via an elventy function in the config', async () => {
|
||||
cli = new RocketCli({
|
||||
argv: [
|
||||
'start',
|
||||
'--config-file',
|
||||
path.join(__dirname, 'e2e-fixtures', 'content', 'eleventy.rocket.config.js'),
|
||||
],
|
||||
});
|
||||
await execute();
|
||||
const indexHtml = await readOutput('index.html', {
|
||||
type: 'start',
|
||||
});
|
||||
it('can modify eleventy via an elventy function in the config', async () => {
|
||||
cli = await executeStart('e2e-fixtures/content/eleventy.rocket.config.js');
|
||||
const indexHtml = await readStartOutput(cli, 'index.html');
|
||||
expect(indexHtml).to.equal(
|
||||
['# BEFORE #', '<p>Content inside <code>docs/index.md</code></p>'].join('\n'),
|
||||
);
|
||||
});
|
||||
|
||||
it('will throw if you try to set options by returning an object', async () => {
|
||||
cli = new RocketCli({
|
||||
argv: [
|
||||
'start',
|
||||
'--config-file',
|
||||
path.join(__dirname, 'e2e-fixtures', 'content', 'eleventy-return.rocket.config.js'),
|
||||
],
|
||||
});
|
||||
|
||||
await expectThrowsAsync(() => execute(), {
|
||||
errorMatch: /Error in your Eleventy config file.*/,
|
||||
});
|
||||
await expectThrowsAsync(
|
||||
() => executeStart('e2e-fixtures/content/eleventy-return.rocket.config.js'),
|
||||
{
|
||||
errorMatch: /Error in your Eleventy config file.*/,
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setupDevAndBuildPlugins in config', () => {
|
||||
it('can add a rollup plugin via setupDevAndBuildPlugins for build command', async () => {
|
||||
cli = new RocketCli({
|
||||
argv: [
|
||||
'build',
|
||||
'--config-file',
|
||||
path.join(__dirname, 'e2e-fixtures', 'rollup-plugin', 'devbuild.rocket.config.js'),
|
||||
],
|
||||
});
|
||||
await execute();
|
||||
const inlineModule = await readOutput('e97af63d.js');
|
||||
cli = await executeBuild('e2e-fixtures/rollup-plugin/devbuild.rocket.config.js');
|
||||
const inlineModule = await readBuildOutput(cli, 'e97af63d.js');
|
||||
expect(inlineModule).to.equal('var a={test:"data"};console.log(a);');
|
||||
});
|
||||
|
||||
it('can add a rollup plugin via setupDevAndBuildPlugins for start command', async () => {
|
||||
cli = new RocketCli({
|
||||
argv: [
|
||||
'start',
|
||||
'--config-file',
|
||||
path.join(__dirname, 'e2e-fixtures', 'rollup-plugin', 'devbuild.rocket.config.js'),
|
||||
],
|
||||
});
|
||||
await execute();
|
||||
cli = await executeStart('e2e-fixtures/rollup-plugin/devbuild.rocket.config.js');
|
||||
|
||||
const response = await fetch('http://localhost:8080/test-data.json');
|
||||
expect(response.ok).to.be.true; // no server error
|
||||
@@ -173,88 +72,45 @@ describe('RocketCli e2e', () => {
|
||||
});
|
||||
|
||||
it('can add a rollup plugin for dev & build and modify a build only plugin via the config', async () => {
|
||||
cli = new RocketCli({
|
||||
argv: [
|
||||
'build',
|
||||
'--config-file',
|
||||
path.join(__dirname, 'e2e-fixtures', 'rollup-plugin', 'devbuild-build.rocket.config.js'),
|
||||
],
|
||||
});
|
||||
await execute();
|
||||
const inlineModule = await readOutput('e97af63d.js');
|
||||
cli = await executeBuild('e2e-fixtures/rollup-plugin/devbuild-build.rocket.config.js');
|
||||
const inlineModule = await readBuildOutput(cli, 'e97af63d.js');
|
||||
expect(inlineModule).to.equal('var a={test:"data"};console.log(a);');
|
||||
|
||||
const swCode = await readOutput('my-service-worker.js');
|
||||
const swCode = await readBuildOutput(cli, 'my-service-worker.js');
|
||||
expect(swCode).to.not.be.undefined;
|
||||
});
|
||||
|
||||
it('can adjust the inputDir', async () => {
|
||||
cli = new RocketCli({
|
||||
argv: [
|
||||
'start',
|
||||
'--config-file',
|
||||
path.join(__dirname, 'e2e-fixtures', 'change-input-dir', 'rocket.config.js'),
|
||||
],
|
||||
});
|
||||
await execute();
|
||||
cli = await executeStart('e2e-fixtures/change-input-dir/rocket.config.js');
|
||||
|
||||
const indexHtml = await readOutput('index.html', {
|
||||
type: 'start',
|
||||
});
|
||||
const indexHtml = await readStartOutput(cli, 'index.html');
|
||||
expect(indexHtml).to.equal('<p>Markdown in <code>docs/page/index.md</code></p>');
|
||||
});
|
||||
|
||||
it('can access main rocket config values via {{rocketConfig.value}}', async () => {
|
||||
cli = new RocketCli({
|
||||
argv: [
|
||||
'start',
|
||||
'--config-file',
|
||||
path.join(__dirname, 'e2e-fixtures', 'rocket-config-in-template', 'rocket.config.js'),
|
||||
],
|
||||
});
|
||||
await execute();
|
||||
cli = await executeStart('e2e-fixtures/rocket-config-in-template/rocket.config.js');
|
||||
|
||||
const indexHtml = await readOutput('index.html', {
|
||||
type: 'start',
|
||||
});
|
||||
const indexHtml = await readStartOutput(cli, 'index.html');
|
||||
expect(indexHtml).to.equal(
|
||||
'<p>You can show Rocket config data like rocketConfig.absoluteBaseUrl = <a href="http://test-domain.com/">http://test-domain.com/</a></p>',
|
||||
);
|
||||
});
|
||||
|
||||
it('can add a pathPrefix that will not influence the start command', async () => {
|
||||
cli = new RocketCli({
|
||||
argv: [
|
||||
'start',
|
||||
'--config-file',
|
||||
path.join(__dirname, 'e2e-fixtures', 'content', 'pathPrefix.rocket.config.js'),
|
||||
],
|
||||
});
|
||||
await execute();
|
||||
cli = await executeStart('e2e-fixtures/content/pathPrefix.rocket.config.js');
|
||||
|
||||
const linkHtml = await readOutput('link/index.html', {
|
||||
type: 'start',
|
||||
});
|
||||
const linkHtml = await readStartOutput(cli, 'link/index.html');
|
||||
expect(linkHtml).to.equal(
|
||||
['<p><a href="../">home</a></p>', '<p><a href="/">absolute home</a></p>'].join('\n'),
|
||||
);
|
||||
const assetHtml = await readOutput('use-assets/index.html', {
|
||||
type: 'start',
|
||||
});
|
||||
const assetHtml = await readStartOutput(cli, 'use-assets/index.html');
|
||||
expect(assetHtml).to.equal('<link rel="stylesheet" href="/_merged_assets/some.css">');
|
||||
});
|
||||
|
||||
it('can add a pathPrefix that will be used in the build command', async () => {
|
||||
cli = new RocketCli({
|
||||
argv: [
|
||||
'build',
|
||||
'--config-file',
|
||||
path.join(__dirname, 'e2e-fixtures', 'content', 'pathPrefix.rocket.config.js'),
|
||||
],
|
||||
});
|
||||
await execute();
|
||||
cli = await executeBuild('e2e-fixtures/content/pathPrefix.rocket.config.js');
|
||||
|
||||
const linkHtml = await readOutput('link/index.html', {
|
||||
const linkHtml = await readBuildOutput(cli, 'link/index.html', {
|
||||
stripServiceWorker: true,
|
||||
stripToBody: true,
|
||||
});
|
||||
@@ -263,7 +119,7 @@ describe('RocketCli e2e', () => {
|
||||
'\n',
|
||||
),
|
||||
);
|
||||
const assetHtml = await readOutput('use-assets/index.html', {
|
||||
const assetHtml = await readBuildOutput(cli, 'use-assets/index.html', {
|
||||
stripServiceWorker: true,
|
||||
});
|
||||
expect(assetHtml).to.equal(
|
||||
|
||||
@@ -6,7 +6,7 @@ import json from '@rollup/plugin-json';
|
||||
import { addPlugin, adjustPluginOptions } from 'plugins-manager';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const outputDir = path.join(__dirname, '..', '__output');
|
||||
const outputDir = path.join(__dirname, '__output');
|
||||
|
||||
/** @type {Partial<import("../../../types/main").RocketCliOptions>} */
|
||||
const config = {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @rocket/core
|
||||
|
||||
## 0.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- be0d0b3: fix: add missing main entry to the packages
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@rocket/core",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
@@ -14,6 +14,7 @@
|
||||
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
|
||||
"homepage": "https://rocket.modern-web.dev/",
|
||||
"type": "module",
|
||||
"main": "./dist/title.cjs",
|
||||
"exports": {
|
||||
"./title": {
|
||||
"require": "./dist/title.cjs",
|
||||
|
||||
@@ -18,6 +18,7 @@ export class RocketDrawer extends OverlayMixin(LitElement) {
|
||||
return {
|
||||
useOverlay: { type: Boolean, reflect: true },
|
||||
useOverlayMediaQuery: { type: String },
|
||||
mediaMatcher: { type: Object },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -89,6 +90,20 @@ export class RocketDrawer extends OverlayMixin(LitElement) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changedProperties.has('useOverlayMediaQuery')) {
|
||||
this.mediaMatcher.removeEventListener('change', this.onMatchMedia);
|
||||
|
||||
this.mediaMatcher = window.matchMedia(this.useOverlayMediaQuery);
|
||||
this.mediaMatcher.addEventListener('change', this.onMatchMedia);
|
||||
this.useOverlay = !!this.mediaMatcher.matches;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param { MediaQueryListEvent } query
|
||||
*/
|
||||
onMatchMedia(query) {
|
||||
this.useOverlay = !!query.matches;
|
||||
}
|
||||
|
||||
_setupOpenCloseListeners() {
|
||||
@@ -118,11 +133,15 @@ export class RocketDrawer extends OverlayMixin(LitElement) {
|
||||
|
||||
this.__toggle = this.__toggle.bind(this);
|
||||
|
||||
this.onMatchMedia = this.onMatchMedia.bind(this);
|
||||
this.onGestureStart = this.onGestureStart.bind(this);
|
||||
this.onGestureMove = this.onGestureMove.bind(this);
|
||||
this.onGestureEnd = this.onGestureEnd.bind(this);
|
||||
this.updateFromTouch = this.updateFromTouch.bind(this);
|
||||
|
||||
this.mediaMatcher = window.matchMedia(this.useOverlayMediaQuery);
|
||||
this.mediaMatcher.addEventListener('change', this.onMatchMedia);
|
||||
|
||||
this._startX = 0;
|
||||
this._currentX = 0;
|
||||
this._velocity = 0;
|
||||
@@ -133,10 +152,7 @@ export class RocketDrawer extends OverlayMixin(LitElement) {
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.useOverlay = !!window.matchMedia(this.useOverlayMediaQuery).matches;
|
||||
window.matchMedia(this.useOverlayMediaQuery).addListener(query => {
|
||||
this.useOverlay = !!query.matches;
|
||||
});
|
||||
this.useOverlay = !!this.mediaMatcher.matches;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -53,13 +53,12 @@ describe('eleventy-plugin-mdjs-unified', () => {
|
||||
|
||||
it('renders markdown with javascript', async () => {
|
||||
const files = await renderEleventy('./test-node/fixtures/mdjs');
|
||||
expect(files).to.deep.equal([
|
||||
{
|
||||
html:
|
||||
'<h1 id="first"><a aria-hidden="true" tabindex="-1" href="#first"><span class="icon icon-link"></span></a>First</h1>\n<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> foo <span class="token operator">=</span> <span class="token string">\'bar\'</span><span class="token punctuation">;</span>\n<span class="token keyword module">import</span> <span class="token punctuation">{</span> html <span class="token punctuation">}</span> <span class="token keyword module">from</span> <span class="token string">\'lit-html\'</span><span class="token punctuation">;</span>\n</code></pre>\n<mdjs-story mdjs-story-name="inline"></mdjs-story>\n<mdjs-preview mdjs-story-name="withBorder"></mdjs-preview>\n <script type="module">\n \nexport const inline = () => html` <p>main</p> `;\nexport const withBorder = () => html` <p>main</p> `;\nconst rootNode = document;\nconst stories = [{ key: \'inline\', story: inline, code: `<pre class="language-js"><code class="language-js"><span class="token keyword module">export</span> <span class="token keyword">const</span> <span class="token function-variable function">inline</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> html<span class="token template-string"><span class="token template-punctuation string">\\`</span><span class="token html language-html"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>main<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> </span><span class="token template-punctuation string">\\`</span></span><span class="token punctuation">;</span>\n</code></pre>` }, { key: \'withBorder\', story: withBorder, code: `<pre class="language-js"><code class="language-js"><span class="token keyword module">export</span> <span class="token keyword">const</span> <span class="token function-variable function">withBorder</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> html<span class="token template-string"><span class="token template-punctuation string">\\`</span><span class="token html language-html"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>main<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> </span><span class="token template-punctuation string">\\`</span></span><span class="token punctuation">;</span>\n</code></pre>` }];\nfor (const story of stories) {\n const storyEl = rootNode.querySelector(`[mdjs-story-name="${story.key}"]`);\n storyEl.codeHasHtml = true;\n storyEl.story = story.story;\n storyEl.code = story.code;\n};\nif (!customElements.get(\'mdjs-preview\')) { import(\'@mdjs/mdjs-preview/mdjs-preview.js\'); }\nif (!customElements.get(\'mdjs-story\')) { import(\'@mdjs/mdjs-story/mdjs-story.js\'); }\n </script>\n ',
|
||||
name: 'first/index.html',
|
||||
},
|
||||
]);
|
||||
|
||||
expect(files.length).to.equal(1);
|
||||
expect(files[0].name).to.equal('first/index.html');
|
||||
|
||||
expect(files[0].html).to.include('<script type="module">');
|
||||
expect(files[0].html).to.include('for (const story of stories)');
|
||||
});
|
||||
|
||||
it('rewrites relative import pathes', async () => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const RocketNav = require('./eleventy-rocket-nav');
|
||||
const { addPageAnchors } = require('./src/addPageAnchors.js');
|
||||
|
||||
// export the configuration function for plugin
|
||||
module.exports = function (eleventyConfig) {
|
||||
@@ -8,6 +9,10 @@ module.exports = function (eleventyConfig) {
|
||||
eleventyConfig.addNunjucksFilter('rocketNavToHtml', function (pages, options) {
|
||||
return RocketNav.toHtml.call(eleventyConfig, pages, options);
|
||||
});
|
||||
eleventyConfig.addTransform('rocket-nav-add-page-anchors', async function (content) {
|
||||
const newContent = await addPageAnchors(content);
|
||||
return newContent;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.navigation = {
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# @rocket/eleventy-rocket-nav
|
||||
|
||||
## 0.3.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- ef8ebb0: To support dynamically created content to be part of the anchor navigation of the page we now analyze the final html output instead of `entry.templateContent`.
|
||||
|
||||
BREAKING CHANGE:
|
||||
|
||||
- only add anchors for the currently active pages (before it added anchor for every page)
|
||||
|
||||
## 0.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -94,21 +94,8 @@ function findNavigationEntries(nodes = [], key = '') {
|
||||
entry.title = entry.key;
|
||||
}
|
||||
if (entry.key) {
|
||||
if (!headingsCache.has(entry.templateContent)) {
|
||||
headingsCache.set(entry.templateContent, getHeadingsOfHtml(entry.templateContent));
|
||||
}
|
||||
const headings = /** @type {Heading[]} */ (headingsCache.get(entry.templateContent));
|
||||
const anchors = headings.map(heading => ({
|
||||
key: heading.text + Math.random(),
|
||||
parent: entry.key,
|
||||
url: `${entry.url}#${heading.id}`,
|
||||
pluginType: 'eleventy-navigation',
|
||||
parentKey: entry.key,
|
||||
title: heading.text,
|
||||
anchor: true,
|
||||
}));
|
||||
// @ts-ignore
|
||||
entry.children = [...anchors, ...findNavigationEntries(nodes, entry.key)];
|
||||
entry.children = findNavigationEntries(nodes, entry.key);
|
||||
}
|
||||
return entry;
|
||||
});
|
||||
@@ -227,43 +214,36 @@ function navigationToHtml(pages, _options = {}) {
|
||||
}>${pages
|
||||
.map(entry => {
|
||||
const liClass = [];
|
||||
const aClass = [];
|
||||
if (options.listItemClass) {
|
||||
liClass.push(options.listItemClass);
|
||||
}
|
||||
if (options.anchorClass) {
|
||||
aClass.push(options.anchorClass);
|
||||
}
|
||||
if (options.activeKey === entry.key) {
|
||||
if (options.activeListItemClass) {
|
||||
liClass.push(options.activeListItemClass);
|
||||
}
|
||||
if (options.activeAnchorClass) {
|
||||
aClass.push(options.activeAnchorClass);
|
||||
}
|
||||
if (options.activeKey === entry.key && options.activeListItemClass) {
|
||||
liClass.push(options.activeListItemClass);
|
||||
}
|
||||
if (options.activeTreeListClass && activePages && activePages.includes(entry.key)) {
|
||||
liClass.push(options.activeTreeListClass);
|
||||
}
|
||||
if (options.activeAnchorListClass && activePages && activePages.includes(entry.key)) {
|
||||
aClass.push(options.activeAnchorListClass);
|
||||
}
|
||||
if (options.listItemHasChildrenClass && entry.children && entry.children.length) {
|
||||
liClass.push(options.listItemHasChildrenClass);
|
||||
}
|
||||
|
||||
if (entry.anchor) {
|
||||
liClass.push('anchor');
|
||||
aClass.push('anchor');
|
||||
const output = [];
|
||||
output.push(
|
||||
`<${options.listItemElement}${liClass.length ? ` class="${liClass.join(' ')}"` : ''}>`,
|
||||
);
|
||||
output.push(`<a href="${urlFilter(entry.url)}">${entry.title}</a>`);
|
||||
if (options.showExcerpt && entry.excerpt) {
|
||||
output.push(`: ${entry.excerpt}`);
|
||||
}
|
||||
if (options.activeKey === entry.key && options.activeListItemClass) {
|
||||
output.push('<!-- ADD PAGE ANCHORS -->');
|
||||
}
|
||||
if (entry.children) {
|
||||
output.push(navigationToHtml(entry.children, options));
|
||||
}
|
||||
output.push(`</${options.listItemElement}>`);
|
||||
|
||||
return `<${options.listItemElement}${
|
||||
liClass.length ? ` class="${liClass.join(' ')}"` : ''
|
||||
}><a href="${urlFilter(entry.url)}"${
|
||||
aClass.length ? ` class="${aClass.join(' ')}"` : ''
|
||||
}>${entry.title}</a>${options.showExcerpt && entry.excerpt ? `: ${entry.excerpt}` : ''}${
|
||||
entry.children ? navigationToHtml(entry.children, options) : ''
|
||||
}</${options.listItemElement}>`;
|
||||
return output.join('\n');
|
||||
})
|
||||
.join('\n')}</${options.listElement}>`
|
||||
: '';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@rocket/eleventy-rocket-nav",
|
||||
"version": "0.2.1",
|
||||
"version": "0.3.0",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
@@ -13,7 +13,7 @@
|
||||
},
|
||||
"main": ".eleventy.js",
|
||||
"scripts": {
|
||||
"test": "mocha test-node/**/*.test.js test-node/*.test.js",
|
||||
"test": "mocha test-node/**/*.test.js test-node/*.test.js --timeout 5000",
|
||||
"test:watch": "mocha test-node/**/*.test.js test-node/*.test.js --watch"
|
||||
},
|
||||
"files": [
|
||||
|
||||
136
packages/eleventy-rocket-nav/src/addPageAnchors.js
Normal file
136
packages/eleventy-rocket-nav/src/addPageAnchors.js
Normal file
@@ -0,0 +1,136 @@
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
const fs = require('fs');
|
||||
const { SaxEventType, SAXParser } = require('sax-wasm');
|
||||
|
||||
const saxPath = require.resolve('sax-wasm/lib/sax-wasm.wasm');
|
||||
const saxWasmBuffer = fs.readFileSync(saxPath);
|
||||
|
||||
/** @typedef {import('../types').Heading} Heading */
|
||||
|
||||
/** @typedef {import('sax-wasm').Text} Text */
|
||||
/** @typedef {import('sax-wasm').Tag} Tag */
|
||||
/** @typedef {import('sax-wasm').Position} Position */
|
||||
|
||||
// Instantiate
|
||||
const parser = new SAXParser(
|
||||
SaxEventType.CloseTag | SaxEventType.Comment,
|
||||
{ highWaterMark: 256 * 1024 }, // 256k chunks
|
||||
);
|
||||
|
||||
/**
|
||||
* @param {object} options
|
||||
* @param {string} options.content
|
||||
* @param {Position} options.start
|
||||
* @param {Position} options.end
|
||||
* @param {string} options.insert
|
||||
*/
|
||||
function removeBetween({ content, start, end, insert = '' }) {
|
||||
const lines = content.split('\n');
|
||||
const i = start.line;
|
||||
const line = lines[i];
|
||||
const upToChange = line.slice(0, start.character - 1);
|
||||
const afterChange = line.slice(end.character + 2);
|
||||
|
||||
lines[i] = `${upToChange}${insert}${afterChange}`;
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Tag} data
|
||||
* @param {string} name
|
||||
*/
|
||||
function getAttribute(data, name) {
|
||||
if (data.attributes) {
|
||||
const { attributes } = data;
|
||||
const foundIndex = attributes.findIndex(entry => entry.name.value === name);
|
||||
if (foundIndex !== -1) {
|
||||
return attributes[foundIndex].value.value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Tag} data
|
||||
*/
|
||||
function getText(data) {
|
||||
if (data.textNodes) {
|
||||
return data.textNodes.map(textNode => textNode.value).join('');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} html
|
||||
*/
|
||||
function getHeadingsOfHtml(html) {
|
||||
/** @type {Heading[]} */
|
||||
const headings = [];
|
||||
/** @type {Text} */
|
||||
let insertPoint;
|
||||
parser.eventHandler = (ev, _data) => {
|
||||
if (ev === SaxEventType.Comment) {
|
||||
const data = /** @type {Text} */ (/** @type {any} */ (_data));
|
||||
// NOTE: we NEED to access data internal value so sax-wasm does not reuse it's value
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const tmp = data.start.line + data.end.line;
|
||||
if (data.value.trim() === 'ADD PAGE ANCHORS' || data.value.trim() === '-->ADD PAGE ANCHORS') {
|
||||
insertPoint = data;
|
||||
}
|
||||
}
|
||||
|
||||
if (ev === SaxEventType.CloseTag) {
|
||||
const data = /** @type {Tag} */ (/** @type {any} */ (_data));
|
||||
if (data.name === 'h2') {
|
||||
const id = getAttribute(data, 'id');
|
||||
const text = getText(data);
|
||||
if (id && text) {
|
||||
headings.push({ text, id });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
parser.write(Buffer.from(html, 'utf8'));
|
||||
parser.end();
|
||||
|
||||
// @ts-ignore
|
||||
return { headings, insertPoint };
|
||||
}
|
||||
|
||||
let isSetup = false;
|
||||
|
||||
/**
|
||||
* @param {string} content
|
||||
*/
|
||||
async function addPageAnchors(content) {
|
||||
if (!isSetup) {
|
||||
await parser.prepareWasm(saxWasmBuffer);
|
||||
isSetup = true;
|
||||
}
|
||||
|
||||
const { headings, insertPoint } = getHeadingsOfHtml(content);
|
||||
const pageAnchorsHtml = [];
|
||||
if (headings.length > 0) {
|
||||
pageAnchorsHtml.push('<ul>');
|
||||
for (const heading of headings) {
|
||||
pageAnchorsHtml.push(' <li class="menu-item anchor">');
|
||||
pageAnchorsHtml.push(` <a href="#${heading.id}" class="anchor">${heading.text}</a>`);
|
||||
pageAnchorsHtml.push(' </li>');
|
||||
}
|
||||
pageAnchorsHtml.push('</ul>');
|
||||
}
|
||||
|
||||
if (insertPoint) {
|
||||
return removeBetween({
|
||||
content,
|
||||
start: insertPoint.start,
|
||||
end: insertPoint.end,
|
||||
insert: pageAnchorsHtml.join('\n'),
|
||||
});
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
addPageAnchors,
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
const { expect } = require('chai');
|
||||
const prettier = require('prettier');
|
||||
const { addPageAnchors } = require('../src/addPageAnchors.js');
|
||||
|
||||
const format = code => prettier.format(code, { parser: 'html' }).trim();
|
||||
|
||||
describe('addPageAnchors', () => {
|
||||
it('finds and adds anchors for each h2 as an unordered list', async () => {
|
||||
const input = [
|
||||
'<body>',
|
||||
' <!-- ADD PAGE ANCHORS -->',
|
||||
' <div id="content">',
|
||||
' <h2 id="first">👉 First Headline</h2>',
|
||||
' </div>',
|
||||
'</body>',
|
||||
].join('\n');
|
||||
const expected = [
|
||||
'<body>',
|
||||
' <ul>',
|
||||
' <li class="menu-item anchor">',
|
||||
' <a href="#first" class="anchor">👉 First Headline</a>',
|
||||
' </li>',
|
||||
' </ul>',
|
||||
' <div id="content">',
|
||||
' <h2 id="first">👉 First Headline</h2>',
|
||||
' </div>',
|
||||
'</body>',
|
||||
].join('\n');
|
||||
const result = await addPageAnchors(input);
|
||||
expect(format(result)).to.deep.equal(expected);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
const eleventyNavigationPlugin = require('@rocket/eleventy-rocket-nav');
|
||||
|
||||
module.exports = function (eleventyConfig) {
|
||||
eleventyConfig.addPlugin(eleventyNavigationPlugin);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = 'layout.njk';
|
||||
@@ -0,0 +1,9 @@
|
||||
<body>
|
||||
{{ collections.all | rocketNav | rocketNavToHtml({
|
||||
listItemClass: "menu-item",
|
||||
activeListItemClass: "current",
|
||||
activeKey: eleventyNavigation.key
|
||||
}) | safe }}
|
||||
|
||||
{{ content | safe }}
|
||||
</body>
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: Bats
|
||||
eleventyNavigation:
|
||||
key: Bats
|
||||
parent: Mammals
|
||||
order: 2
|
||||
---
|
||||
|
||||
🦇 can fly.
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
title: Humans
|
||||
eleventyNavigation:
|
||||
key: Humans
|
||||
parent: Mammals
|
||||
order: 1
|
||||
---
|
||||
|
||||
<h2 id="anatomy">Anatomy</h2>
|
||||
<p>Has arms.</p>
|
||||
<h2 id="age">📖 Age</h2>
|
||||
<p>Up to 130 years.</p>
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Mammals
|
||||
eleventyNavigation:
|
||||
key: Mammals
|
||||
---
|
||||
|
||||
Mammals need air.
|
||||
96
packages/eleventy-rocket-nav/test-node/integration.test.js
Normal file
96
packages/eleventy-rocket-nav/test-node/integration.test.js
Normal file
@@ -0,0 +1,96 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const { expect } = require('chai');
|
||||
const Eleventy = require('@11ty/eleventy');
|
||||
const TemplateConfig = require('@11ty/eleventy/src/TemplateConfig');
|
||||
const prettier = require('prettier');
|
||||
|
||||
async function execute(fixtureDir) {
|
||||
const relPath = path.relative(process.cwd(), __dirname);
|
||||
const relativeInputPath = path.join(relPath, fixtureDir.split('/').join(path.sep));
|
||||
const relativeOutputPath = path.join(relPath, 'fixtures', '__output');
|
||||
const relativeConfigPath = path.join(relativeInputPath, '.eleventy.js');
|
||||
|
||||
await fs.emptyDir(relativeOutputPath);
|
||||
|
||||
const elev = new Eleventy(relativeInputPath, relativeOutputPath);
|
||||
const config = new TemplateConfig(null, relativeConfigPath);
|
||||
elev.config = config.getConfig();
|
||||
elev.setConfigPathOverride(relativeConfigPath);
|
||||
elev.resetConfig();
|
||||
|
||||
await elev.init();
|
||||
await elev.write();
|
||||
return {
|
||||
readOutput: async readPath => {
|
||||
const relativeReadPath = path.join(relativeOutputPath, readPath);
|
||||
let text = await fs.promises.readFile(relativeReadPath);
|
||||
text = text.toString();
|
||||
text = prettier.format(text, { parser: 'html', printWidth: 100 });
|
||||
return text.trim();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe('eleventy-rocket-nav', () => {
|
||||
it('renders a menu with anchors for h2 content', async () => {
|
||||
const { readOutput } = await execute('fixtures/three-pages');
|
||||
|
||||
const bats = await readOutput('bats/index.html');
|
||||
expect(bats).to.deep.equal(
|
||||
[
|
||||
'<body>',
|
||||
' <ul>',
|
||||
' <li class="menu-item active">',
|
||||
' <a href="/mammals/">Mammals</a>',
|
||||
' <ul>',
|
||||
' <li class="menu-item">',
|
||||
' <a href="/humans/">Humans</a>',
|
||||
' </li>',
|
||||
' <li class="menu-item current">',
|
||||
' <a href="/bats/">Bats</a>',
|
||||
' </li>',
|
||||
' </ul>',
|
||||
' </li>',
|
||||
' </ul>',
|
||||
'',
|
||||
' <p>🦇 can fly.</p>',
|
||||
'</body>',
|
||||
].join('\n'),
|
||||
);
|
||||
|
||||
const humans = await readOutput('humans/index.html');
|
||||
expect(humans).to.deep.equal(
|
||||
[
|
||||
'<body>',
|
||||
' <ul>',
|
||||
' <li class="menu-item active">',
|
||||
' <a href="/mammals/">Mammals</a>',
|
||||
' <ul>',
|
||||
' <li class="menu-item current">',
|
||||
' <a href="/humans/">Humans</a>',
|
||||
' <ul>',
|
||||
' <li class="menu-item anchor">',
|
||||
' <a href="#anatomy" class="anchor">Anatomy</a>',
|
||||
' </li>',
|
||||
' <li class="menu-item anchor">',
|
||||
' <a href="#age" class="anchor">📖 Age</a>',
|
||||
' </li>',
|
||||
' </ul>',
|
||||
' </li>',
|
||||
' <li class="menu-item">',
|
||||
' <a href="/bats/">Bats</a>',
|
||||
' </li>',
|
||||
' </ul>',
|
||||
' </li>',
|
||||
' </ul>',
|
||||
'',
|
||||
' <h2 id="anatomy">Anatomy</h2>',
|
||||
' <p>Has arms.</p>',
|
||||
' <h2 id="age">📖 Age</h2>',
|
||||
' <p>Up to 130 years.</p>',
|
||||
'</body>',
|
||||
].join('\n'),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -19,6 +19,7 @@
|
||||
".": "./index.js",
|
||||
"./preset/": "./preset/",
|
||||
"./inline-notification": "./inline-notification/index.js",
|
||||
"./inline-notification-element": "./inline-notification/inline-notification.js",
|
||||
"./inline-notification/inline-notification.js": "./inline-notification/inline-notification.js"
|
||||
},
|
||||
"scripts": {
|
||||
@@ -36,6 +37,6 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@rocket/drawer": "^0.1.2",
|
||||
"@rocket/navigation": "^0.2.0"
|
||||
"@rocket/navigation": "^0.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,12 +43,12 @@
|
||||
"remark"
|
||||
],
|
||||
"dependencies": {
|
||||
"@mdjs/mdjs-preview": "^0.3.0",
|
||||
"@mdjs/mdjs-story": "^0.1.0",
|
||||
"@mdjs/mdjs-preview": "^0.3.2",
|
||||
"@mdjs/mdjs-story": "^0.1.2",
|
||||
"@types/unist": "^2.0.3",
|
||||
"es-module-lexer": "^0.3.26",
|
||||
"github-markdown-css": "^4.0.0",
|
||||
"plugins-manager": "^0.2.0",
|
||||
"plugins-manager": "^0.2.1",
|
||||
"rehype-autolink-headings": "^5.0.1",
|
||||
"rehype-prism-template": "^0.4.1",
|
||||
"rehype-raw": "^5.0.0",
|
||||
|
||||
@@ -18,6 +18,7 @@ const { executeSetupFunctions } = require('plugins-manager');
|
||||
|
||||
const { mdjsParse } = require('./mdjsParse.js');
|
||||
const { mdjsStoryParse } = require('./mdjsStoryParse.js');
|
||||
const { mdjsSetupCode } = require('./mdjsSetupCode.js');
|
||||
|
||||
/** @type {MdjsProcessPlugin[]} */
|
||||
const defaultMetaPlugins = [
|
||||
@@ -25,6 +26,7 @@ const defaultMetaPlugins = [
|
||||
{ name: 'gfm', plugin: gfm },
|
||||
{ name: 'mdjsParse', plugin: mdjsParse },
|
||||
{ name: 'mdjsStoryParse', plugin: mdjsStoryParse },
|
||||
{ name: 'mdjsSetupCode', plugin: mdjsSetupCode },
|
||||
// @ts-ignore
|
||||
{ name: 'remark2rehype', plugin: remark2rehype, options: { allowDangerousHtml: true } },
|
||||
// @ts-ignore
|
||||
@@ -50,29 +52,15 @@ const defaultMetaPlugins = [
|
||||
* @param {function[]} [options.setupUnifiedPlugins]
|
||||
* @param {MdjsProcessPlugin[]} [options.plugins] deprecated option use setupUnifiedPlugins instead
|
||||
*/
|
||||
async function mdjsProcess(
|
||||
mdjs,
|
||||
{ rootNodeQueryCode = 'document', setupUnifiedPlugins = [] } = {},
|
||||
) {
|
||||
async function mdjsProcess(mdjs, { setupUnifiedPlugins = [] } = {}) {
|
||||
const parser = unified();
|
||||
|
||||
const metaPlugins = executeSetupFunctions(setupUnifiedPlugins, defaultMetaPlugins);
|
||||
|
||||
// @ts-ignore
|
||||
for (const pluginObj of metaPlugins) {
|
||||
parser.use(pluginObj.plugin, pluginObj.options);
|
||||
}
|
||||
|
||||
/** @type {unknown} */
|
||||
const parseResult = await parser.process(mdjs);
|
||||
const result = /** @type {ParseResult} */ (parseResult);
|
||||
|
||||
const { stories, jsCode } = result.data;
|
||||
let fullJsCode = jsCode;
|
||||
|
||||
if (stories && stories.length > 0) {
|
||||
const storiesCode = stories.map(story => story.code).join('\n');
|
||||
|
||||
/**
|
||||
* @param {string} code
|
||||
*/
|
||||
async function highlightCode(code) {
|
||||
// @ts-ignore
|
||||
const codePlugins = metaPlugins.filter(pluginObj =>
|
||||
['markdown', 'remark2rehype', 'rehypePrism', 'htmlStringify'].includes(pluginObj.name),
|
||||
@@ -82,46 +70,30 @@ async function mdjsProcess(
|
||||
for (const pluginObj of codePlugins) {
|
||||
codeParser.use(pluginObj.plugin, pluginObj.options);
|
||||
}
|
||||
|
||||
const invokeStoriesCode = [];
|
||||
for (const story of stories) {
|
||||
let code = '';
|
||||
switch (story.type) {
|
||||
case 'html':
|
||||
code = `\`\`\`html\n${story.code.split('`')[1]}\n\`\`\``;
|
||||
break;
|
||||
case 'js':
|
||||
code = `\`\`\`js\n${story.code}\n\`\`\``;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
const codeResult = await codeParser.process(code);
|
||||
const highlightedCode = /** @type {string} */ (codeResult.contents)
|
||||
.replace(/`/g, '\\`')
|
||||
.replace(/\$/g, '\\$');
|
||||
invokeStoriesCode.push(
|
||||
`{ key: '${story.key}', story: ${story.key}, code: \`${highlightedCode}\` }`,
|
||||
);
|
||||
}
|
||||
|
||||
fullJsCode = [
|
||||
jsCode,
|
||||
storiesCode,
|
||||
`const rootNode = ${rootNodeQueryCode};`,
|
||||
`const stories = [${invokeStoriesCode.join(', ')}];`,
|
||||
`for (const story of stories) {`,
|
||||
// eslint-disable-next-line no-template-curly-in-string
|
||||
' const storyEl = rootNode.querySelector(`[mdjs-story-name="${story.key}"]`);',
|
||||
` storyEl.codeHasHtml = true;`,
|
||||
` storyEl.story = story.story;`,
|
||||
` storyEl.code = story.code;`,
|
||||
`};`,
|
||||
`if (!customElements.get('mdjs-preview')) { import('@mdjs/mdjs-preview/mdjs-preview.js'); }`,
|
||||
`if (!customElements.get('mdjs-story')) { import('@mdjs/mdjs-story/mdjs-story.js'); }`,
|
||||
].join('\n');
|
||||
const codeResult = await codeParser.process(code);
|
||||
return codeResult.contents;
|
||||
}
|
||||
return { stories, jsCode: fullJsCode, html: result.contents };
|
||||
|
||||
// @ts-ignore
|
||||
for (const pluginObj of metaPlugins) {
|
||||
if (pluginObj.name === 'mdjsSetupCode') {
|
||||
if (pluginObj.options && !pluginObj.options.highlightCode) {
|
||||
pluginObj.options.highlightCode = highlightCode;
|
||||
}
|
||||
if (!pluginObj.options) {
|
||||
pluginObj.options = { highlightCode };
|
||||
}
|
||||
}
|
||||
parser.use(pluginObj.plugin, pluginObj.options);
|
||||
}
|
||||
|
||||
/** @type {unknown} */
|
||||
const parseResult = await parser.process(mdjs);
|
||||
const result = /** @type {ParseResult} */ (parseResult);
|
||||
|
||||
const { stories, setupJsCode } = result.data;
|
||||
|
||||
return { stories, jsCode: setupJsCode, html: result.contents };
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
68
packages/mdjs-core/src/mdjsSetupCode.js
Normal file
68
packages/mdjs-core/src/mdjsSetupCode.js
Normal file
@@ -0,0 +1,68 @@
|
||||
/** @typedef {import('vfile').VFileOptions} VFileOptions */
|
||||
/** @typedef {import('unist').Node} Node */
|
||||
/** @typedef {import('@mdjs/core/types/code').Story} Story */
|
||||
|
||||
function mdjsSetupCode({
|
||||
rootNodeQueryCode = 'document',
|
||||
highlightCode = /** @param {string} code */ code => code,
|
||||
} = {}) {
|
||||
/**
|
||||
* @param {Node} tree
|
||||
* @param {VFileOptions} file
|
||||
*/
|
||||
async function transformer(tree, file) {
|
||||
const { stories, jsCode } = file.data;
|
||||
|
||||
file.data.setupJsCode = jsCode;
|
||||
|
||||
if (stories && stories.length > 0) {
|
||||
const storiesCode = stories.map(/** @param {Story} story */ story => story.code).join('\n');
|
||||
|
||||
const invokeStoriesCode = [];
|
||||
for (const story of stories) {
|
||||
let code = '';
|
||||
switch (story.type) {
|
||||
case 'html':
|
||||
code = `\`\`\`html\n${story.code.split('`')[1]}\n\`\`\``;
|
||||
break;
|
||||
case 'js':
|
||||
code = `\`\`\`js\n${story.code}\n\`\`\``;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
let highlightedCode = await highlightCode(code);
|
||||
highlightedCode = highlightedCode.replace(/`/g, '\\`').replace(/\$/g, '\\$');
|
||||
invokeStoriesCode.push(
|
||||
`{ key: '${story.key}', story: ${story.key}, code: \`${highlightedCode}\` }`,
|
||||
);
|
||||
}
|
||||
|
||||
file.data.setupJsCode = [
|
||||
jsCode,
|
||||
storiesCode,
|
||||
`const rootNode = ${rootNodeQueryCode};`,
|
||||
`const stories = [${invokeStoriesCode.join(', ')}];`,
|
||||
`for (const story of stories) {`,
|
||||
// eslint-disable-next-line no-template-curly-in-string
|
||||
' const storyEl = rootNode.querySelector(`[mdjs-story-name="${story.key}"]`);',
|
||||
` storyEl.codeHasHtml = true;`,
|
||||
` storyEl.story = story.story;`,
|
||||
` storyEl.code = story.code;`,
|
||||
` storyEl.jsCode = \`${jsCode.replace(/`/g, '\\`')}\`;`,
|
||||
`};`,
|
||||
`if (!customElements.get('mdjs-preview')) { import('@mdjs/mdjs-preview/mdjs-preview.js'); }`,
|
||||
`if (!customElements.get('mdjs-story')) { import('@mdjs/mdjs-story/mdjs-story.js'); }`,
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
return transformer;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
mdjsSetupCode,
|
||||
};
|
||||
@@ -43,12 +43,14 @@ describe('mdjsProcess', () => {
|
||||
' storyEl.codeHasHtml = true;',
|
||||
' storyEl.story = story.story;',
|
||||
' storyEl.code = story.code;',
|
||||
' storyEl.jsCode = `const bar = 2;`;',
|
||||
'};',
|
||||
`if (!customElements.get('mdjs-preview')) { import('@mdjs/mdjs-preview/mdjs-preview.js'); }`,
|
||||
`if (!customElements.get('mdjs-story')) { import('@mdjs/mdjs-story/mdjs-story.js'); }`,
|
||||
].join('\n');
|
||||
|
||||
const result = await mdjsProcess(input);
|
||||
|
||||
expect(result.html).to.equal(expected);
|
||||
expect(result.jsCode).to.equal(expectedJsCode);
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('mdjsParse', () => {
|
||||
expect(/** @type {MDJSVFileData} */ (result.data).jsCode).to.equal('const bar = 22;');
|
||||
});
|
||||
|
||||
// TODO: fix this bug
|
||||
// TODO: fix this bug - maybe something in unified itself 🤔
|
||||
it.skip('handling only "js script" code blocks', async () => {
|
||||
const input = [
|
||||
//
|
||||
|
||||
1
packages/mdjs-core/types/code.d.ts
vendored
1
packages/mdjs-core/types/code.d.ts
vendored
@@ -25,6 +25,7 @@ export interface ParseResult {
|
||||
data: {
|
||||
stories: Story[];
|
||||
jsCode: string;
|
||||
setupJsCode: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
# @mdjs/mdjs-preview
|
||||
|
||||
## 0.3.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- be0d0b3: fix: add missing main entry to the packages
|
||||
|
||||
## 0.3.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ee6b404: Pass on the shadowRoot to the story function
|
||||
|
||||
## 0.3.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 15e0abe: Clean up dependencies - add Types
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mdjs/mdjs-preview",
|
||||
"version": "0.3.0",
|
||||
"version": "0.3.2",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
@@ -13,6 +13,7 @@
|
||||
},
|
||||
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
|
||||
"homepage": "https://rocket.modern-web.dev/docs/markdown-javascript/preview/",
|
||||
"main": "./index.js",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./index.js",
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
import { LitElement, html, css } from 'lit-element';
|
||||
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
|
||||
|
||||
/**
|
||||
* @typedef {object} StoryOptions
|
||||
* @property {ShadowRoot | null} StoryOptions.shadowRoot
|
||||
*/
|
||||
|
||||
/** @typedef {(options?: StoryOptions) => ReturnType<LitElement['render']>} LitHtmlStoryFn */
|
||||
|
||||
/**
|
||||
* Renders a story within a preview frame
|
||||
*
|
||||
* @element mdjs-preview
|
||||
* @prop {StoryFn} [story=(() => TemplateResult)] Function that returns the story
|
||||
*/
|
||||
export class MdJsPreview extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
@@ -28,6 +41,7 @@ export class MdJsPreview extends LitElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.code = '';
|
||||
/** @type {LitHtmlStoryFn} */
|
||||
this.story = () => html` <p>Loading...</p> `;
|
||||
this.codeHasHtml = false;
|
||||
}
|
||||
@@ -35,7 +49,7 @@ export class MdJsPreview extends LitElement {
|
||||
render() {
|
||||
return html`
|
||||
<div id="wrapper">
|
||||
<div>${this.story()}</div>
|
||||
<div>${this.story({ shadowRoot: this.shadowRoot })}</div>
|
||||
<button id="showCodeButton" @click=${this.toggleShowCode}>show code</button>
|
||||
</div>
|
||||
${this.codeHasHtml ? unsafeHTML(this.code) : html`<pre><code>${this.code}</code></pre>`}
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
# @mdjs/mdjs-story
|
||||
|
||||
## 0.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- be0d0b3: fix: add missing main entry to the packages
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ee6b404: Pass on the shadowRoot to the story function
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 15e0abe: Clean up dependencies - add Types
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mdjs/mdjs-story",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.2",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
@@ -13,6 +13,7 @@
|
||||
},
|
||||
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
|
||||
"homepage": "https://rocket.modern-web.dev/docs/markdown-javascript/story/",
|
||||
"main": "./index.js",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./index.js",
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
import { LitElement, html } from 'lit-element';
|
||||
|
||||
/**
|
||||
* @typedef {object} StoryOptions
|
||||
* @property {ShadowRoot | null} StoryOptions.shadowRoot
|
||||
*/
|
||||
|
||||
/** @typedef {(options?: StoryOptions) => ReturnType<LitElement['render']>} LitHtmlStoryFn */
|
||||
|
||||
/**
|
||||
* Renders a story
|
||||
*
|
||||
* @element mdjs-story
|
||||
* @prop {StoryFn} [story=(() => TemplateResult)] Function that returns the story
|
||||
*/
|
||||
export class MdJsStory extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
@@ -11,10 +24,11 @@ export class MdJsStory extends LitElement {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.story = () => html` <p>Loading...</p> `;
|
||||
/** @type {LitHtmlStoryFn} */
|
||||
this.story = () => html`<p>Loading...</p>`;
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.story();
|
||||
return this.story({ shadowRoot: this.shadowRoot });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @rocket/navigation
|
||||
|
||||
## 0.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 728a205: feat(navigation): add no-redirects attribute
|
||||
|
||||
By default, the sidebar nav redirects clicks on category headings to
|
||||
their first child.
|
||||
|
||||
To disable those redirects, override
|
||||
\_includes/\_joiningBlocks/\_layoutSidebar/sidebar/20-navigation.njk
|
||||
and add the no-redirects attribute to the <rocket-navigation>
|
||||
element.
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@rocket/navigation",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
|
||||
@@ -1,3 +1,27 @@
|
||||
/**
|
||||
* Debounce a function
|
||||
* @template {(this: any, ...args: any[]) => void} T
|
||||
* @param {T} func function
|
||||
* @param {number} wait time in milliseconds to debounce
|
||||
* @param {boolean} immediate when true, run immediately and on the leading edge
|
||||
* @return {T} debounced function
|
||||
*/
|
||||
function debounce(func, wait, immediate) {
|
||||
/** @type {number|undefined} */
|
||||
let timeout;
|
||||
return /** @type {typeof func}*/ (function () {
|
||||
let args = /** @type {Parameters<typeof func>} */ (/** @type {unknown}*/ (arguments));
|
||||
const later = () => {
|
||||
timeout = undefined;
|
||||
if (!immediate) func.apply(this, args);
|
||||
};
|
||||
const callNow = immediate && !timeout;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
if (callNow) func.apply(this, args);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {object} NavigationListItem
|
||||
* @property {HTMLElement} headline
|
||||
@@ -5,12 +29,17 @@
|
||||
* @property {number} top
|
||||
*/
|
||||
|
||||
/**
|
||||
* @element rocket-navigation
|
||||
* @attr {Boolean} no-redirects - if set, will not redirect to first child of nav category when clicking on category header.
|
||||
*/
|
||||
export class RocketNavigation extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
/** @type NavigationListItem[] */
|
||||
this.list = [];
|
||||
this.__scrollHandler = this.__scrollHandler.bind(this);
|
||||
this.__clickHandler = this.__clickHandler.bind(this);
|
||||
this.__scrollHandler = debounce(this.__scrollHandler.bind(this), 25, true);
|
||||
this.__isSetup = false;
|
||||
}
|
||||
|
||||
@@ -20,27 +49,7 @@ export class RocketNavigation extends HTMLElement {
|
||||
}
|
||||
this.__isSetup = true;
|
||||
|
||||
this.addEventListener('click', ev => {
|
||||
const el = /** @type {HTMLElement} */ (ev.target);
|
||||
if (el.classList.contains('anchor')) {
|
||||
const anchor = /** @type {HTMLAnchorElement} */ (el);
|
||||
ev.preventDefault();
|
||||
this.dispatchEvent(new Event('close-overlay', { bubbles: true }));
|
||||
// wait for closing animation to finish before start scrolling
|
||||
setTimeout(() => {
|
||||
const parsedUrl = new URL(anchor.href);
|
||||
document.location.hash = parsedUrl.hash;
|
||||
}, 250);
|
||||
}
|
||||
const links = el.parentElement?.querySelectorAll('ul a');
|
||||
if (links && links.length > 1) {
|
||||
const subLink = /** @type {HTMLAnchorElement} */ (links[1]);
|
||||
if (!subLink.classList.contains('anchor')) {
|
||||
ev.preventDefault();
|
||||
subLink.click();
|
||||
}
|
||||
}
|
||||
});
|
||||
this.addEventListener('click', this.__clickHandler);
|
||||
|
||||
const anchors = /** @type {NodeListOf<HTMLAnchorElement>} */ (this.querySelectorAll(
|
||||
'li.current a.anchor',
|
||||
@@ -57,12 +66,41 @@ export class RocketNavigation extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: debounce
|
||||
window.addEventListener('scroll', this.__scrollHandler);
|
||||
window.addEventListener('scroll', this.__scrollHandler, { passive: true });
|
||||
|
||||
this.__scrollHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Event} ev
|
||||
*/
|
||||
__clickHandler(ev) {
|
||||
const el = /** @type {HTMLElement} */ (ev.target);
|
||||
if (el.classList.contains('anchor')) {
|
||||
const anchor =
|
||||
el instanceof HTMLAnchorElement
|
||||
? el
|
||||
: /** @type{HTMLAnchorElement} */ (el.querySelector('a.anchor'));
|
||||
ev.preventDefault();
|
||||
this.dispatchEvent(new Event('close-overlay', { bubbles: true }));
|
||||
// wait for closing animation to finish before start scrolling
|
||||
setTimeout(() => {
|
||||
const parsedUrl = new URL(anchor.href);
|
||||
document.location.hash = parsedUrl.hash;
|
||||
}, 250);
|
||||
}
|
||||
if (!this.hasAttribute('no-redirects')) {
|
||||
const links = el.parentElement?.querySelectorAll('ul a');
|
||||
if (links && links.length > 1) {
|
||||
const subLink = /** @type {HTMLAnchorElement} */ (links[1]);
|
||||
if (!subLink.classList.contains('anchor')) {
|
||||
ev.preventDefault();
|
||||
subLink.click();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__scrollHandler() {
|
||||
for (const listObj of this.list) {
|
||||
listObj.top = listObj.headline.getBoundingClientRect().top;
|
||||
|
||||
@@ -58,7 +58,8 @@ describe('rocket-navigation', () => {
|
||||
expect(anchorSpy).to.not.be.called;
|
||||
});
|
||||
|
||||
it('will mark the currently "active" headline in the menu', async () => {
|
||||
it('will mark the currently "active" headline in the menu', async function () {
|
||||
this.timeout(5000);
|
||||
function addBlock(headline, length = 5) {
|
||||
return html`
|
||||
<h2 id="${headline}">${headline}</h2>
|
||||
@@ -96,20 +97,20 @@ describe('rocket-navigation', () => {
|
||||
}
|
||||
</style>
|
||||
`);
|
||||
await aTimeout(0);
|
||||
await aTimeout(50);
|
||||
const anchorLis = wrapper.querySelectorAll('.menu-item.anchor');
|
||||
expect(anchorLis[0]).to.have.class('current');
|
||||
expect(anchorLis[1]).to.not.have.class('current');
|
||||
expect(anchorLis[2]).to.not.have.class('current');
|
||||
|
||||
document.querySelector('#middle').scrollIntoView();
|
||||
await aTimeout(20);
|
||||
await aTimeout(100);
|
||||
expect(anchorLis[0]).to.not.have.class('current');
|
||||
expect(anchorLis[1]).to.have.class('current');
|
||||
expect(anchorLis[2]).to.not.have.class('current');
|
||||
|
||||
document.querySelector('#bottom').scrollIntoView();
|
||||
await aTimeout(20);
|
||||
await aTimeout(100);
|
||||
expect(anchorLis[0]).to.not.have.class('current');
|
||||
expect(anchorLis[1]).to.not.have.class('current');
|
||||
expect(anchorLis[2]).to.have.class('current');
|
||||
|
||||
3
packages/playground/README.md
Normal file
3
packages/playground/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Story element for mdjs
|
||||
|
||||
For docs please see our homepage [https://rocket.modern-web.dev/docs/markdown-javascript/story/](https://rocket.modern-web.dev/docs/markdown-javascript/story/).
|
||||
1
packages/playground/index.js
Normal file
1
packages/playground/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { RocketPlayground } from './src/RocketPlayground.js';
|
||||
40
packages/playground/package.json
Normal file
40
packages/playground/package.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "@rocket/playground",
|
||||
"version": "0.1.1",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"description": "Rendering storybook story functions inside a story window with show code capabilities",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/modernweb-dev/rocket.git",
|
||||
"directory": "packages/mdjs-story"
|
||||
},
|
||||
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
|
||||
"homepage": "https://rocket.modern-web.dev/docs/markdown-javascript/story/",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./index.js",
|
||||
"./rocket-playground": "./rocket-playground.js"
|
||||
},
|
||||
"scripts": {
|
||||
"debug": "cd ../../ && npm run debug -- --group mdjs-story",
|
||||
"test": "npm run test:web",
|
||||
"test:watch": "onchange 'src/**/*.{js,cjs}' 'test-node/**/*.js' -- npm run test:node",
|
||||
"test:node": "mocha --timeout 5000 test-node/**/*.test.{js,cjs} test-node/*.test.{js,cjs}",
|
||||
"test:web": "cd ../../ && npm run test:web -- --group mdjs-story"
|
||||
},
|
||||
"files": [
|
||||
"*.js",
|
||||
"assets",
|
||||
"dist-types",
|
||||
"src"
|
||||
],
|
||||
"dependencies": {
|
||||
"@vanillawc/wc-monaco-editor": "^1.10.12",
|
||||
"lit-element": "^2.4.0",
|
||||
"wasm-flate": "^1.0.2-browser"
|
||||
},
|
||||
"types": "dist-types/index.d.ts"
|
||||
}
|
||||
3
packages/playground/rocket-playground.js
Normal file
3
packages/playground/rocket-playground.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import { RocketPlayground } from './src/RocketPlayground.js';
|
||||
|
||||
customElements.define('rocket-playground', RocketPlayground);
|
||||
133
packages/playground/src/DevicePreview.js
Normal file
133
packages/playground/src/DevicePreview.js
Normal file
@@ -0,0 +1,133 @@
|
||||
import { LitElement, html, css } from 'lit-element';
|
||||
import frame from './frame.svg.js';
|
||||
|
||||
const DEVICES = {
|
||||
contentFlow: {
|
||||
name: 'Content Flow',
|
||||
system: 'web',
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
dpr: 1,
|
||||
},
|
||||
webSmall: {
|
||||
name: 'Web Small',
|
||||
system: 'web',
|
||||
width: 360,
|
||||
height: 640,
|
||||
dpr: 1,
|
||||
},
|
||||
pixel2: {
|
||||
name: 'Pixel 2',
|
||||
system: 'android',
|
||||
width: 411,
|
||||
height: 731,
|
||||
dpr: 2.6,
|
||||
},
|
||||
galaxyS5: {
|
||||
name: 'Galaxy 5',
|
||||
system: 'android',
|
||||
width: 360,
|
||||
height: 640,
|
||||
dpr: 3,
|
||||
},
|
||||
iphoneX: {
|
||||
name: 'iPhone X',
|
||||
system: 'ios',
|
||||
width: 375,
|
||||
height: 812,
|
||||
dpr: 3,
|
||||
},
|
||||
};
|
||||
|
||||
export class DevicePreview extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
jsCode: { type: String },
|
||||
device: { type: String, reflect: true },
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.jsCode = '';
|
||||
this.device = 'pixel2';
|
||||
this.iframe = null;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return [
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
iframe {
|
||||
border: none;
|
||||
background: #fff;
|
||||
background: green;
|
||||
}
|
||||
:host([device='pixel2']) iframe {
|
||||
width: 411px;
|
||||
height: 640px;
|
||||
}
|
||||
div,
|
||||
iframe {
|
||||
position: absolute;
|
||||
}
|
||||
iframe {
|
||||
top: 100px;
|
||||
left: 100px;
|
||||
}
|
||||
svg {
|
||||
width: 587px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
update(changedProperties) {
|
||||
super.update(changedProperties);
|
||||
|
||||
if (this.iframe) {
|
||||
const iframeContent = `
|
||||
<html>
|
||||
<head>
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"lit-html": "http://localhost:8000/__wds-outside-root__/1/node_modules/lit-html/lit-html.js",
|
||||
"@rocket/launch/inline-notification-element": "http://localhost:8000/__wds-outside-root__/1/packages/launch/inline-notification/inline-notification.js"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
<script type="module">
|
||||
${this.jsCode}
|
||||
</script>
|
||||
<body></body>
|
||||
</html>
|
||||
`;
|
||||
this.iframe.src = `data:text/html;charset=utf-8,${encodeURIComponent(iframeContent)}`;
|
||||
}
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this.iframe = this.shadowRoot.querySelector('iframe');
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<iframe
|
||||
sandbox="allow-scripts"
|
||||
csp="script-src localhost:8000 'unsafe-inline'; connect-src 'none'"
|
||||
></iframe>
|
||||
<div>${frame(html)}</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
170
packages/playground/src/RocketPlayground.js
Normal file
170
packages/playground/src/RocketPlayground.js
Normal file
@@ -0,0 +1,170 @@
|
||||
import { LitElement, html, css } from 'lit-element';
|
||||
import '@vanillawc/wc-monaco-editor';
|
||||
import './device-preview.js';
|
||||
|
||||
/**
|
||||
* @typedef {object} StoryOptions
|
||||
* @property {ShadowRoot | null} StoryOptions.shadowRoot
|
||||
*/
|
||||
|
||||
/** @typedef {(options?: StoryOptions) => ReturnType<LitElement['render']>} LitHtmlStoryFn */
|
||||
|
||||
/** @typedef {import('./devices.js').devices} devices */
|
||||
|
||||
export function createViewer(jsCode) {
|
||||
const iframeViewer = document.createElement('iframe');
|
||||
const iframeContent = `
|
||||
<html>
|
||||
<head>
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"lit-html": "http://localhost:8000/__wds-outside-root__/1/node_modules/lit-html/lit-html.js"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
<script type="module">
|
||||
${jsCode}
|
||||
</script>
|
||||
<body></body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
iframeViewer.setAttribute('style', `border: none; background: #fff;`);
|
||||
iframeViewer.setAttribute('sandbox', 'allow-scripts');
|
||||
iframeViewer.setAttribute('csp', "script-src localhost:8000 'unsafe-inline'; connect-src 'none'");
|
||||
|
||||
// Uses a data url as when using srcdoc the iframe csp rules get ignored?
|
||||
// iframeViewer.setAttribute('srcdoc', iframeContent);
|
||||
iframeViewer.src = `data:text/html;charset=utf-8,${escape(iframeContent)}`;
|
||||
|
||||
return iframeViewer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a story
|
||||
*
|
||||
* @element mdjs-story
|
||||
* @prop {StoryFn} [story=(() => TemplateResult)] Function that returns the story
|
||||
*/
|
||||
export class RocketPlayground extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
story: {
|
||||
attribute: false,
|
||||
},
|
||||
jsCode: {
|
||||
type: String,
|
||||
},
|
||||
showDevices: {
|
||||
type: Array,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
/** @type {LitHtmlStoryFn} */
|
||||
this.story = () => html` <p>Loading...</p> `;
|
||||
|
||||
this.jsCode = '';
|
||||
|
||||
// this.story({ shadowRoot: this.shadowRoot });
|
||||
|
||||
this.urlState = new URLSearchParams(document.location.search);
|
||||
// this.urlState.append('jsCode', '');
|
||||
|
||||
/**
|
||||
* @type {Array<keyof devices>}
|
||||
*/
|
||||
this.showDevices = ['pixel2'];
|
||||
}
|
||||
|
||||
setJsCode(jsCode) {
|
||||
const encoded = flate.gzip_encode(jsCode);
|
||||
this.urlState.set('jsCode', encoded);
|
||||
this.updateUrl();
|
||||
this.jsCode = jsCode;
|
||||
}
|
||||
|
||||
updateUrl() {
|
||||
history.pushState('', '', '?' + this.urlState.toString());
|
||||
}
|
||||
|
||||
// createRenderRoot() {
|
||||
// return this;
|
||||
// }
|
||||
|
||||
setup() {
|
||||
this.editorWc = this.querySelector('wc-monaco-editor');
|
||||
if (this.editorWc) {
|
||||
this.editorWc.tabSize = 2;
|
||||
|
||||
if (this.urlState.get('jsCode')) {
|
||||
this.jsCode = flate.gzip_decode(this.urlState.get('jsCode'));
|
||||
}
|
||||
|
||||
const value = [
|
||||
"import { html, render } from 'lit-html';",
|
||||
'export const foo = () => html`',
|
||||
' <p>hey there</p>',
|
||||
'`;',
|
||||
"render(foo(), document.querySelector('body'))",
|
||||
].join('\n');
|
||||
this.editorWc.value = this.jsCode || value;
|
||||
|
||||
this.editorWc.editor.getModel().onDidChangeRawContentFast(() => {
|
||||
this.setJsCode(this.editorWc.value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
setTimeout(() => {
|
||||
this.setup();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.editorWc = document.createElement('wc-monaco-editor');
|
||||
this.editorWc.slot = 'editor';
|
||||
this.editorWc.setAttribute('language', 'javascript');
|
||||
this.appendChild(this.editorWc);
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return [
|
||||
css`
|
||||
:host {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
#editor-wrapper,
|
||||
#devices-wrapper {
|
||||
width: 50%;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div id="editor-wrapper">
|
||||
<slot name="editor"></slot>
|
||||
</div>
|
||||
<div id="devices-wrapper">
|
||||
${this.showDevices.map(
|
||||
device => html` <device-preview .jsCode=${this.jsCode} .device=${device}></device-preview> `,
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
export async function createImportMapForLocalPackages(packages) {
|
||||
for (const pkg of packages) {
|
||||
const pkgJson = await import(pkg);
|
||||
console.log({pkg, pkgJson});
|
||||
}
|
||||
}
|
||||
3
packages/playground/src/device-preview.js
Normal file
3
packages/playground/src/device-preview.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import { DevicePreview } from './DevicePreview.js';
|
||||
|
||||
customElements.define('device-preview', DevicePreview);
|
||||
37
packages/playground/src/devices.js
Normal file
37
packages/playground/src/devices.js
Normal file
@@ -0,0 +1,37 @@
|
||||
export const devices = {
|
||||
contentFlow: {
|
||||
name: 'Content Flow',
|
||||
system: 'web',
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
dpr: 1,
|
||||
},
|
||||
webSmall: {
|
||||
name: 'Web Small',
|
||||
system: 'web',
|
||||
width: 360,
|
||||
height: 640,
|
||||
dpr: 1,
|
||||
},
|
||||
pixel2: {
|
||||
name: 'Pixel 2',
|
||||
system: 'android',
|
||||
width: 411,
|
||||
height: 731,
|
||||
dpr: 2.6,
|
||||
},
|
||||
galaxyS5: {
|
||||
name: 'Galaxy 5',
|
||||
system: 'android',
|
||||
width: 360,
|
||||
height: 640,
|
||||
dpr: 3,
|
||||
},
|
||||
iphoneX: {
|
||||
name: 'iPhone X',
|
||||
system: 'ios',
|
||||
width: 375,
|
||||
height: 812,
|
||||
dpr: 3,
|
||||
},
|
||||
};
|
||||
27
packages/playground/src/frame.svg.js
Normal file
27
packages/playground/src/frame.svg.js
Normal file
@@ -0,0 +1,27 @@
|
||||
export default tag => tag`
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 583.782 992.748">
|
||||
<defs>
|
||||
<path id="reuse-0" d="M-328.071 77.934h87.321c3.158 3.241 3.966 6.64 0 10.357h-87.321c-2.388-2.89-5.443-5.624 0-10.357z" filter="url(#c)"/>
|
||||
</defs>
|
||||
<defs>
|
||||
<filter id="c" width="1.02" height="1.18" x="-.01" y="-.09" color-interpolation-filters="sRGB">
|
||||
<feGaussianBlur stdDeviation=".388"/>
|
||||
</filter>
|
||||
<filter id="b" width="1.146" height="1.072" x="-.073" y="-.036" color-interpolation-filters="sRGB">
|
||||
<feGaussianBlur stdDeviation="14.425"/>
|
||||
</filter>
|
||||
<linearGradient id="a">
|
||||
<stop offset="0" stop-color="#ececec"/>
|
||||
<stop offset="1" stop-color="#ececec" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g transform="translate(-83.389 -12.472)">
|
||||
<path fill-rule="evenodd" d="M121.945 32.979c-25.884 7.036-44.412 19.814-46.138 45.707l-.543 819.843c.093 25.7 10.39 46.875 47.222 56.291l413.614-.48c29.367-6.096 48.726-21.391 48.851-54.367l.543-818.4c-3.437-20.23-8.931-39.683-45.05-47.632zm431.2 46.298a3.26 3.26 0 013.267 3.258l.572 825.24a3.26 3.26 0 01-3.263 3.262l-459.512-.562a3.26 3.26 0 01-3.256-3.264l.567-823.549a3.26 3.26 0 013.251-3.258z" filter="url(#b)" opacity=".45" transform="translate(83.389 12.471) scale(.9375)"/>
|
||||
<path fill="none" d="M325.826 41.2v964.02h-197.99c-23.97-6.931-42.562-21.032-44.447-57.409l.505-854.589c1.73-26.554 13.299-45.975 41.921-53.033zm-.052 0v964.02h197.99c23.97-6.931 42.562-21.032 44.447-57.409l-.505-854.589c-1.73-26.554-13.299-45.975-41.922-53.033z"/>
|
||||
<path fill="#151515" fill-rule="evenodd" d="M193.16 44.552c-24.085 6.596-41.324 18.577-42.93 42.852l-.506 768.63c.088 24.094 9.67 43.949 43.942 52.777l384.868-.45c27.326-5.715 45.34-20.057 45.456-50.973l.505-767.278c-3.198-18.965-8.31-37.204-41.92-44.656zm395.232 66.864a2.863 2.863 0 012.871 2.862l.504 724.803a2.863 2.863 0 01-2.868 2.866l-403.586-.495a2.863 2.863 0 01-2.86-2.865l.498-723.318a2.863 2.863 0 012.856-2.862z"/>
|
||||
<use filter="url(#c)" transform="matrix(1.00398 0 0 1.14592 671.843 -16.339)" xlink:href="#reuse-0"/>
|
||||
<ellipse cx="532.319" cy="77.836" rx="8.207" ry="7.955"/>
|
||||
<use filter="url(#c)" transform="matrix(1.00398 0 0 1.14592 671.843 778.763)" xlink:href="#reuse-0"/>
|
||||
</g>
|
||||
</svg>
|
||||
`;
|
||||
0
packages/playground/src/sample.js
Normal file
0
packages/playground/src/sample.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import chai from 'chai';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { createImportMapForLocalPackages } from '../src/createImportMapForLocalPackages.js';
|
||||
|
||||
const { expect } = chai;
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
describe('normalizeConfig', () => {
|
||||
it.only('makes sure essential settings are there', async () => {
|
||||
const importMap = await createImportMapForLocalPackages(['@rocket/launch']);
|
||||
|
||||
expect(importMap).to.deep.equal({
|
||||
imports: {
|
||||
"@rocket/launch/inline-notification-element": "http://localhost:8000/__wds-outside-root__/1/packages/launch/inline-notification/inline-notification.js"
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
24
packages/playground/tsconfig.json
Normal file
24
packages/playground/tsconfig.json
Normal file
@@ -0,0 +1,24 @@
|
||||
// Don't edit this file directly. It is generated by /scripts/update-package-configs.ts
|
||||
|
||||
{
|
||||
"extends": "../../tsconfig.browser-base.json",
|
||||
"compilerOptions": {
|
||||
"module": "ESNext",
|
||||
"outDir": "./dist-types",
|
||||
"rootDir": ".",
|
||||
"composite": true,
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"emitDeclarationOnly": true
|
||||
},
|
||||
"references": [],
|
||||
"include": [
|
||||
"src",
|
||||
"*.js",
|
||||
"types"
|
||||
],
|
||||
"exclude": [
|
||||
"dist",
|
||||
"dist-types"
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
# plugins-manager
|
||||
|
||||
## 0.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- be0d0b3: fix: add missing main entry to the packages
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "plugins-manager",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
@@ -14,6 +14,7 @@
|
||||
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
|
||||
"homepage": "https://rocket.modern-web.dev/docs/tools/plugins-manager/",
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"exports": {
|
||||
".": {
|
||||
"require": "./dist/index.cjs",
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @rocket/search
|
||||
|
||||
## 0.3.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 302227e: Add variable for border-radius of SearchCombobox
|
||||
|
||||
## 0.3.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- be0d0b3: fix: add missing main entry to the packages
|
||||
- Updated dependencies [be0d0b3]
|
||||
- plugins-manager@0.2.1
|
||||
|
||||
## 0.3.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@rocket/search",
|
||||
"version": "0.3.0",
|
||||
"version": "0.3.2",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
@@ -14,6 +14,7 @@
|
||||
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
|
||||
"homepage": "https://rocket.modern-web.dev/docs/presets/search/",
|
||||
"type": "module",
|
||||
"main": "./node.js",
|
||||
"exports": {
|
||||
".": "./node.js",
|
||||
"./node": "./node.js",
|
||||
@@ -44,7 +45,7 @@
|
||||
"@open-wc/scoped-elements": "^1.3.2",
|
||||
"chalk": "^4.0.0",
|
||||
"minisearch": "^3.0.2",
|
||||
"plugins-manager": "^0.2.0",
|
||||
"plugins-manager": "^0.2.1",
|
||||
"sax-wasm": "^2.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ function getTitle({ result, search }) {
|
||||
function getText({ result, search }) {
|
||||
const { terms, body } = result;
|
||||
|
||||
return highlightSearchTerms({ text: body, search, terms });
|
||||
return highlightSearchTerms({ text: body, search, terms, addEllipsis: true });
|
||||
}
|
||||
|
||||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110
|
||||
@@ -40,6 +40,7 @@ export class RocketSearch extends ScopedElementsMixin(LitElement) {
|
||||
search: { type: String },
|
||||
results: { type: Array },
|
||||
maxResults: { type: Number, attribute: 'max-results' },
|
||||
noResultsText: { type: String },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -55,6 +56,7 @@ export class RocketSearch extends ScopedElementsMixin(LitElement) {
|
||||
this.jsonUrl = '';
|
||||
this.search = '';
|
||||
this.maxResults = 10;
|
||||
this.noResultsText = 'No results found';
|
||||
/**
|
||||
* @type {RocketSearchResult[]}
|
||||
*/
|
||||
@@ -110,7 +112,7 @@ export class RocketSearch extends ScopedElementsMixin(LitElement) {
|
||||
return html`
|
||||
<rocket-search-combobox
|
||||
name="combo"
|
||||
label="Rocket Search"
|
||||
label="Search"
|
||||
@input=${ev => {
|
||||
this.search = ev.target.value;
|
||||
}}
|
||||
@@ -132,6 +134,9 @@ export class RocketSearch extends ScopedElementsMixin(LitElement) {
|
||||
></rocket-search-option>
|
||||
`,
|
||||
)}
|
||||
${this.results.length <= 0 && this.search.length > 0
|
||||
? html` <rocket-search-option .title=${this.noResultsText}></rocket-search-option> `
|
||||
: ''}
|
||||
</rocket-search-combobox>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ export class RocketSearchCombobox extends LionCombobox {
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
fill: var(--rocket-search-fill-color, #000);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
::slotted([slot='prefix'][close-btn]) {
|
||||
@@ -136,7 +138,7 @@ export class RocketSearchCombobox extends LionCombobox {
|
||||
display: flex;
|
||||
border: 1px solid var(--rocket-search-input-border-color, #dfe1e5);
|
||||
box-shadow: none;
|
||||
border-radius: 24px;
|
||||
border-radius: var(--rocket-search-input-border-radius, 24px);
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ export class RocketSearchOption extends LinkMixin(LionOption) {
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
cursor: default;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
padding: 12px 10px;
|
||||
display: flex;
|
||||
@@ -103,7 +103,7 @@ export class RocketSearchOption extends LinkMixin(LionOption) {
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<img class="icon" src=${getIcon(this.section)} />
|
||||
<img class="icon" src=${getIcon(this.section)} alt=${this.section} />
|
||||
<div class="choice-field__label">
|
||||
<div class="title">${unsafeHTML(this.title)}</div>
|
||||
<div class="text">${unsafeHTML(this.text)}</div>
|
||||
|
||||
@@ -26,6 +26,7 @@ function defaultHighlight(term) {
|
||||
* @param {number} [options.before]
|
||||
* @param {number} [options.length]
|
||||
* @param {function} [options.highlight]
|
||||
* @param {boolean} [options.addEllipsis]
|
||||
*/
|
||||
export function highlightSearchTerms({
|
||||
search,
|
||||
@@ -34,6 +35,7 @@ export function highlightSearchTerms({
|
||||
before = 15,
|
||||
length = 100,
|
||||
highlight = defaultHighlight,
|
||||
addEllipsis = false,
|
||||
}) {
|
||||
if (!search || !text) {
|
||||
return '';
|
||||
@@ -70,5 +72,9 @@ export function highlightSearchTerms({
|
||||
} while (startIndex !== -1);
|
||||
}
|
||||
|
||||
return newText.substr(truncateStart, length + extraLength);
|
||||
let textResult = newText.substr(truncateStart, length + extraLength);
|
||||
if (addEllipsis && truncateStart > 0) {
|
||||
textResult = `...${textResult}`;
|
||||
}
|
||||
return textResult;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,10 @@ import { rocketBlog } from '@rocket/blog';
|
||||
import { rocketSearch } from '@rocket/search';
|
||||
import { absoluteBaseUrlNetlify } from '@rocket/core/helpers';
|
||||
|
||||
export default {
|
||||
/** @type {Partial<import("./packages/cli/types/main").RocketCliOptions>} */
|
||||
const config = {
|
||||
presets: [rocketLaunch(), rocketBlog(), rocketSearch()],
|
||||
absoluteBaseUrl: absoluteBaseUrlNetlify('http://localhost:8080'),
|
||||
|
||||
// emptyOutputDir: false,
|
||||
};
|
||||
}
|
||||
export default config;
|
||||
|
||||
10
yarn.lock
10
yarn.lock
@@ -1778,6 +1778,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44"
|
||||
integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==
|
||||
|
||||
"@vanillawc/wc-monaco-editor@^1.10.12":
|
||||
version "1.10.12"
|
||||
resolved "https://registry.yarnpkg.com/@vanillawc/wc-monaco-editor/-/wc-monaco-editor-1.10.12.tgz#73ae976b27fecfbef034df0cd2ea3608f4457180"
|
||||
integrity sha512-UOFs6eCf30qWQE8J4+e6axlcoZAKfa/rOgcqMB70s7UvAzQYnE0zpbWmTyRPCG6ARmuHXDkaxjkG8DXywZfJDA==
|
||||
|
||||
"@web/browser-logs@^0.2.0":
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@web/browser-logs/-/browser-logs-0.2.0.tgz#3f39d59154bf668f0bce467026354ff2b9f3e06b"
|
||||
@@ -8744,6 +8749,11 @@ void-elements@^2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
|
||||
integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=
|
||||
|
||||
wasm-flate@^1.0.2-browser:
|
||||
version "1.0.2-browser"
|
||||
resolved "https://registry.yarnpkg.com/wasm-flate/-/wasm-flate-1.0.2-browser.tgz#e10e758b37c3d38829b54809fa2fef853b48131c"
|
||||
integrity sha512-qpqOzvbKtgG/2Mb0DYXVPQ2nOrkKYoAQtpr4QZzgO8SMfKieeODUy2vomMnimQjH/K2RGWnw+kZmO8yf+Yh9qA==
|
||||
|
||||
wcwidth@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
|
||||
|
||||
Reference in New Issue
Block a user