Compare commits

...

121 Commits

Author SHA1 Message Date
github-actions[bot]
d08692c7f3 Version Packages 2021-04-29 12:35:04 +02:00
Thomas Allmer
2b7f1ee719 fix: support pathprefix in cli, launch, mdjs, search 2021-04-29 12:30:47 +02:00
github-actions[bot]
3802778be4 Version Packages 2021-04-29 08:30:57 +02:00
Thomas Allmer
26f4a1ebff chore: align versions 2021-04-29 00:35:26 +02:00
Thomas Allmer
81edf45fe2 fix: drastically reduce the amount of js files in build output 2021-04-29 00:35:26 +02:00
github-actions[bot]
c5a1d7e8d1 Version Packages 2021-04-24 17:12:15 +02:00
Thomas Allmer
74dd8d1bcc fix(mdjs-preview): autoheight will not grow bigger than the current size height 2021-04-24 17:05:20 +02:00
Thomas Allmer
72f631ac86 chore: add releases 2021-04-23 13:06:44 +02:00
Thomas Allmer
fafb99b0fa feat(mdjs-preview): add a copy code button 2021-04-23 13:06:44 +02:00
Thomas Allmer
f5769b9aa9 fix(mdjs-preview): improve customization capabilities 2021-04-23 13:06:44 +02:00
Konstantinos Norgias
12d9cc3b44 fix: configure simulator themes & platforms 2021-04-23 11:37:34 +02:00
Konstantinos Norgias
ef9b373aa1 style: add color theming with css custom props 2021-04-23 11:37:34 +02:00
Konstantinos Norgias
560234d663 fix: default no render empty themes and platforms 2021-04-23 11:37:34 +02:00
Konstantinos Norgias
024514e901 style: add simulator css vars 2021-04-23 11:37:34 +02:00
Mathieu Puech
66c2d781e6 fix: windows path issue when using rocket lint 2021-04-23 11:18:53 +02:00
github-actions[bot]
14721d1e0f Version Packages 2021-04-20 12:58:41 +02:00
Thomas Allmer
0f6709ac4b fix(mdjs-preview): initial setting should come from the element 2021-04-20 12:55:32 +02:00
Thomas Allmer
ed86ff2346 fix(cli): do not set data-localize-lang in the simulator iframe html tag 2021-04-20 12:55:32 +02:00
Mathieu Puech
c675820163 fix: windows path issue avoid filtering of index section of collections 2021-04-20 12:28:19 +02:00
Konstantinos Norgias
f4a0ab559f fix: add changeset & update drawer 2021-04-20 12:27:12 +02:00
Konstantinos Norgias
a8cdaebab1 fix(simulator): document shadowRoot n/a in iframe 2021-04-20 12:27:12 +02:00
github-actions[bot]
22393dd172 Version Packages 2021-04-20 06:50:18 +02:00
Thomas Allmer
a6fdb31f1e fix(mdjs-preview): do not restory empty values 2021-04-19 23:55:52 +02:00
Thomas Allmer
dd15d4fc65 chore: fix tests 2021-04-19 23:55:52 +02:00
Thomas Allmer
edb1abf82b feat(mdjs-preview): rework preview and add a simulation mode 2021-04-19 23:55:52 +02:00
Thomas Allmer
0b6411661e chore: update lion dependencies for drawer and search 2021-04-19 23:55:52 +02:00
Thomas Allmer
604a80e6cb feat(mdjs-story): force /define entrypoint 2021-04-19 23:55:52 +02:00
Thomas Allmer
fe6a929f1e feat(mdjs-core): keep the original code block and wrap it for preview story 2021-04-19 23:55:52 +02:00
Thomas Allmer
2267e728cf feat(eleventy-plugin-mdjs-unified): write mdjs javascript to file instead of inline 2021-04-19 23:55:52 +02:00
Thomas Allmer
abc8a02b72 fix(cli): supporting an absolute path for the rootDir 2021-04-19 23:55:52 +02:00
Thomas Allmer
2270887faf chore: format package.json 2021-04-19 23:55:52 +02:00
Thomas Allmer
bad4686506 feat(building-rollup): preserve export names & attributes on script tags, 2021-04-19 23:55:52 +02:00
Thomas Allmer
818caad7cb Create chilled-turkeys-help.md 2021-04-04 18:01:06 +02:00
Konstantinos Norgias
672b7e893e chore: generalize label & add alt when no img 2021-04-04 18:01:06 +02:00
Thomas Allmer
a8e66d84f4 feat(mdjs-core): extract mdjsSetupCode function which support a highlightCode fn 2021-04-04 18:00:26 +02:00
github-actions[bot]
e9090d64b9 Version Packages 2021-04-01 20:01:47 +02:00
Benny Powers
728a205b7b chore: add changeset 2021-04-01 19:44:43 +02:00
Benny Powers
67ba29d45a 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.
2021-04-01 19:44:43 +02:00
github-actions[bot]
758caffdf9 Version Packages 2021-03-25 07:14:15 +01:00
qa46hx
302227e8a9 feat(search): add variable for border-radius of SearchCombobox 2021-03-24 23:20:56 +01:00
Thomas Allmer
04a31220fb chore: align versions across the mono repo 2021-03-15 21:03:07 +01:00
Benny Powers
d44a23af0c Merge pull request #83 from modernweb-dev/changeset-release/main
Version Packages
2021-03-07 10:14:39 +02:00
github-actions[bot]
18a79589c2 Version Packages 2021-03-06 19:28:35 +00:00
Thomas Allmer
b7727b0e10 chore: add rocket nav upgrade to cli 2021-03-06 20:26:44 +01:00
Thomas Allmer
5edc40fed5 feat(cli): make sure each instance has its own eleventy config 2021-03-06 19:58:09 +01:00
Amin Yahyaabadi
be0d0b3ca1 fix: add missing main entry to the packages (#81)
This allows the tools to work properly. For example, eslint-plugin-import, TypeScript LSP hyperclick, and many other tools rely on main.
2021-03-06 19:10:49 +01:00
Thomas Allmer
ef8ebb0098 feat(eleventy-rocket-nav): support dynamically created content 2021-03-06 19:05:00 +01:00
djlauk
2fa61e1377 chore: tiny fixes to the README (#74) 2021-02-23 21:45:41 +01:00
Matsuuu
b23e37f38e feat(search): Precache search results to service worker 2021-02-23 21:44:53 +01:00
Matsuuu
cf45e32702 feat(search): Add ellipsis as prefix when truncating 2021-02-23 21:44:53 +01:00
Matsuuu
b5965c6c37 feat(search): Set cursor to pointer on result hover 2021-02-23 21:44:53 +01:00
Matsuuu
e39cc45d23 fix(search): Center search icon 2021-02-23 21:44:53 +01:00
Matsuuu
f0434cb12c feat(search): Add feedback when no results found 2021-02-23 21:44:53 +01:00
Matsuuu
c87caaed2d feat: Allow overlay query modification in Drawer (#73) 2021-02-23 21:31:12 +01:00
Thomas Allmer
04af7ecf53 chore: align dependency versions 2021-02-23 20:39:37 +01:00
github-actions[bot]
98d6aad12a Version Packages 2021-02-05 13:30:10 +01:00
Thomas Allmer
ee6b404aaa fix: mdjs element pass on shadowRoot to story function 2021-02-05 13:26:42 +01:00
Thomas Allmer
8ba8939c67 chore: use test-helper everywhere 2021-02-04 20:48:08 +01:00
Thomas Allmer
8e095b792e fix(cli): watching user preset files to trigger updates automatically 2021-02-04 19:56:56 +01:00
github-actions[bot]
b58ac27658 Version Packages 2021-02-04 09:51:19 +01:00
Thomas Allmer
f44a0f4fd4 fix(cli): rewrite dynamic imports with "`" 2021-02-04 09:45:35 +01:00
Thomas Allmer
750418bb51 fix: use class node api of check-html-links in rocket 2021-02-03 23:17:29 +01:00
Guillaume Grossetie
bc2698c1ba resolves #54 introduce a ignoreLinkPatterns option 2021-02-03 20:43:02 +01:00
Thomas Allmer
74f7ddf478 fix: create social media images only during build 2021-02-03 20:39:16 +01:00
github-actions[bot]
b5fa7ad9af Version Packages 2021-02-01 19:59:49 +01:00
Jorge del Casar
e3abdd956a chore: rename meta plugin to joiningBlocks 2021-02-01 13:20:02 +01:00
Thomas Allmer
8bdc326e38 chore: add changeset and docs 2021-02-01 13:20:02 +01:00
Thomas Allmer
806fcc0556 chore: go back to inline scripts for now 2021-02-01 13:20:02 +01:00
Thomas Allmer
e7d7945259 feat: default layout for index files with open menu button 2021-02-01 13:20:02 +01:00
Thomas Allmer
502347aa8d chore: remove previous/next link for now 2021-02-01 13:20:02 +01:00
Thomas Allmer
26b558c118 fix: mobile navigation 2021-02-01 13:20:02 +01:00
Thomas Allmer
8eec69f918 feat: big refactor to introduce _joiningBlocks 2021-02-01 13:20:02 +01:00
Thomas Allmer
078cff26fe fix(cli): template merge order by numbers, add tests, docs 2021-02-01 13:20:02 +01:00
Jorge del Casar
8896c0655b feat: dynamic partial includes 2021-02-01 13:20:02 +01:00
Stephen Wade
43ef209bad chore: Docs updates (#53)
* chore: fix Eleventy spelling
* chore: misc CONTRIBUTING.md updates
* chore: fix GitHub capitalization
* chore: fix HTML capitalization
* chore: fix JavaScript capitalization
* chore: misc README.md updates
* chore: update slack.md to match modernweb-dev/web
* chore: misc docs/about updates
* chore: misc docs/blog updates
* chore: misc docs/docs updates
* chore: fix mdjs capitalization
* chore: fix Markdown capitalization
* chore: fix Rocket capitalization
* chore: fix apostrophes
* chore: fix TypeScript capitalization
* chore: consistently capitalize headings
* chore: misc docs/guides updates
* chore: misc packages/building-rollup updates
* chore: misc packages/check-html-links updates
* chore: misc packages/cli updates
* chore: misc packages/plugins-manager updates
* chore: misc packages/search updates
* chore: fix tests
* chore: fix lints
2021-01-31 11:00:53 +01:00
github-actions[bot]
f2a4b80f1e Version Packages 2021-01-28 09:10:39 +01:00
Thomas Allmer
f343c5030a fix(check-html-links): read and parse with the same increased highWaterMark 2021-01-28 09:02:56 +01:00
Thomas Allmer
a7b0dbbce0 chore: add blog introducing check-html-links 2021-01-27 19:58:49 +01:00
Jorge del Casar
eeb51c830c chore: build packages on setup 2021-01-25 16:08:57 +01:00
github-actions[bot]
b968badf43 Version Packages 2021-01-21 19:54:12 +01:00
Thomas Allmer
c92769a145 fix(cli): processing of links/assets urls needs to be utf8 safe 2021-01-21 19:48:23 +01:00
Thomas Allmer
562e91fc43 fix(cli): make sure there is no <?xml in the logo code 2021-01-21 16:46:39 +01:00
github-actions[bot]
ffd06fcee9 Version Packages 2021-01-21 01:17:19 +01:00
Thomas Allmer
0eb507d7ef feat: add api for social media images 2021-01-21 01:12:22 +01:00
github-actions[bot]
45cd7206f1 Version Packages 2021-01-20 16:38:31 +01:00
Thomas Allmer
eb74110dd8 Create good-mice-act.md 2021-01-19 23:13:16 +01:00
Julien Lengrand-Lambert
517c7780ab feat: Add some extra info when running html-links
* Add number of files checked
* Add number of links checked
2021-01-19 23:13:16 +01:00
Thomas Allmer
e4852db673 chore: add docs on how to create your own preset 2021-01-19 12:45:31 +01:00
github-actions[bot]
c6c564ede2 Version Packages 2021-01-18 12:14:13 +01:00
Thomas Allmer
a498a5da44 fix(cli): *index.md should not be treated as folder index files 2021-01-18 12:11:46 +01:00
github-actions[bot]
eb6a23dc6a Version Packages 2021-01-17 19:40:29 +01:00
Thomas Allmer
23027bb684 chore: adjust launch release message 2021-01-17 19:20:50 +01:00
Thomas Allmer
cd22231806 chore: adjustments for the restructured CLI Plugin System 2021-01-17 19:09:27 +01:00
Thomas Allmer
b1f61c7759 feat(cli): restructure CLI plugin system - add rocket lint 2021-01-17 19:09:27 +01:00
Thomas Allmer
741f695106 feat: new package check-html-links 2021-01-17 19:09:27 +01:00
github-actions[bot]
156719f977 Version Packages 2021-01-13 14:21:53 +01:00
Thomas Allmer
295cfbdbd8 chore: better support for windows 2021-01-13 14:16:36 +01:00
Thomas Allmer
7dd6f4c64f fix(launch): default logo should not break social images 2021-01-13 14:16:36 +01:00
Thomas Allmer
b68923b608 fix(mdjs-core): use gfm to support markdown tables 2021-01-13 14:16:36 +01:00
Benny Powers
86c3a4b0e8 feat(launch): themability for inline-notification 2021-01-13 13:48:37 +01:00
Benny Powers
897892d6f9 chore: update dependencies
chore: patch typescript error in @lion/overlays

chore: changeset for deps
2021-01-13 13:40:25 +01:00
Benny Powers
7b2dc6430f fix(search): improve a11y
- a11y: add labels to buttons
- perf: makes all templates static
- fix: address typescript errors

chore: changeset for search
2021-01-13 13:40:25 +01:00
github-actions[bot]
2a400e09da Version Packages 2021-01-12 01:53:08 +01:00
Benny Powers
25adb741d8 feat(launch): add icons for discord and telegram 2021-01-12 01:48:24 +01:00
Thomas Allmer
485827127d feat: support relative links + src urls for all 11ty templates 2021-01-12 01:38:34 +01:00
Thomas Allmer
ef3b846bb9 feat: auto create social media images 2021-01-10 12:54:52 +01:00
Benny Powers
6922161429 Merge pull request #17 from modernweb-dev/changeset-release/main
Version Packages
2021-01-10 12:12:05 +02:00
github-actions[bot]
06843f3fa9 Version Packages 2021-01-08 13:36:53 +00:00
Benny Powers
496a1b0974 chore: version launch package 2021-01-08 14:35:32 +01:00
Benny Powers
13c15346b1 fix(launch): pass a11y audit
allows rocket sites to pass the netlify-plugin-a11y audit
2021-01-08 14:35:32 +01:00
github-actions[bot]
8eeebbc978 Version Packages 2021-01-07 16:50:42 +01:00
Thomas Allmer
32f39ae96a fix(cli): while watching an update should not hide navigation 2021-01-07 16:47:51 +01:00
github-actions[bot]
579ecfde50 Version Packages 2021-01-07 09:10:15 +01:00
Mathieu Puech
9aa3265ebb docs: small fixes on CONTRIBUTING.md 2021-01-07 08:54:17 +01:00
Mathieu Puech
d955b436b6 fix(drawer): reset translation on teardown overlay controller 2021-01-07 08:54:17 +01:00
Thomas Allmer
3468ff9fc2 fix(cli): rollup-plugin-html needs to be aware of the pathPrefix 2021-01-07 08:49:40 +01:00
github-actions[bot]
fd4bc27f16 Version Packages 2021-01-06 00:49:41 +01:00
Thomas Allmer
641c7e551c feat: add pathPrefix option for subfolder deployment 2021-01-06 00:46:39 +01:00
github-actions[bot]
f9ae2b8208 Version Packages 2021-01-05 07:35:10 +01:00
Thomas Allmer
a8c7173758 fix: only apply the rollup wrap of dev server plugins if needed 2021-01-05 07:25:44 +01:00
Thomas Allmer
dd5c772ba3 chore: add info about the need of .eleventyignore 2021-01-03 23:26:29 +01:00
465 changed files with 9296 additions and 2264 deletions

View File

@@ -1,2 +1,4 @@
node_modules/**
/docs/_assets/head.html
/docs/_assets
/docs/_includes
/docs/_data

View File

@@ -4,27 +4,27 @@
> Please note that this project is released with a [Contributor Code of Conduct](./CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
First, create a fork of the [modernweb-dev/rocket](https://github.com/modernweb-dev/rocket) repo by hitting the `fork` button on the GitHub page.
First, create a fork of the [modernweb-dev/rocket](https://github.com/modernweb-dev/rocket) repository by hitting the `fork` button on the GitHub page.
Next, clone our repository onto your computer with this command (replacing YOUR_USERNAME with your actual GitHub username)
Next, clone our repository onto your computer.
```sh
git clone git@github.com:modernweb-dev/rocket.git
```
Once cloning is complete, change directory to the repo.
Once cloning is complete, change directory to the repository.
```sh
cd web
cd rocket
```
Now add your fork as a remote
Now add your fork as a remote (replacing YOUR_USERNAME with your GitHub username).
```sh
git remote add fork git@github.com:<YOUR_NAME>/rocket.git
git remote add fork git@github.com:<YOUR_USERNAME>/rocket.git
```
Create a new local branch
Create a new local branch.
```sh
git checkout -b my-awesome-fix
@@ -32,7 +32,7 @@ git checkout -b my-awesome-fix
## Preparing Your Local Environment for Development
Now that you have cloned the repository, ensure you have [yarn](https://classic.yarnpkg.com/lang/en/) installed run the following commands to set up the development environment.
Now that you have cloned the repository, ensure you have [yarn](https://classic.yarnpkg.com/lang/en/) installed, then run the following commands to set up the development environment.
```sh
yarn install
@@ -42,25 +42,25 @@ This will download and install all packages needed.
## Making Your Changes
Make your changes to the project. Commits are linted using precommit hooks, meaning that any code that raises linting error cannot be committed. In order to help avoid that, we recommend using an IDE or editor with an eslint plugin in order to streamline the development process. Plugins are available for all the popular editors. For more information see [ESLint Integrations](https://eslint.org/docs/user-guide/integrations)
Make your changes to the project. Commits are linted using precommit hooks, meaning that any code that raises a linting error cannot be committed. In order to help avoid that, we recommend using an IDE or editor with an ESLint plugin in order to streamline the development process. Plugins are available for all the popular editors. For more information see [ESLint Integrations](https://eslint.org/docs/user-guide/integrations)
### Compiling the typescript code
### Compiling the TypeScript Code
If you're making cross-package changes, you need to compile the typescript code. We recommend executing `tsc:watch` from the root of the package and keeping that running while you make your changes.
If you're making cross-package changes, you need to compile the TypeScript code. We recommend executing `tsc:watch` from the root of the package and keeping that running while you make your changes.
### Running tests
### Running Tests
To run the tests of a package, it's recommended to `cd` into the package directory and then using `yarn test` to run them. This way you're only running tests of that specific package.
### Integration testing
### Integration Testing
To see how your changes integrate with everything together you can use the `test-runner` package. There are different commands in this package which you can execute to trigger different scenarios in the test runner.
## Adding new packages
## Adding New Packages
For all projects the tsconfig/jsconfig configuration files are auto generated. You need to add an entry to the [./workspace-packages.ts](./workspace-packages.ts) to let it generate a config for you. After adding an entry, run `yarn update-package-configs` to generate the files for you.
## Create a Changeset
## Creating a Changeset
If you made changes for which you want to trigger a release, you need to create a changeset.
This documents your intent to release, and allows you to specify a message that will be put into the changelog(s) of the package(s).
@@ -89,7 +89,7 @@ Exceptions:
## Committing Your Changes
Commit messages must follow the [conventional commit format](https://www.conventionalcommits.org/en/v1.0.0-beta.2/)
Commit messages must follow the [conventional commit format](https://www.conventionalcommits.org/en/v1.0.0/)
Modern-web uses package name as scope. So for example if you fix a _terrible bug_ in the package `@web/test-runner`, the commit message should look like this:
```sh

View File

@@ -1,4 +1,4 @@
> This project is in it's BETA phase
> This project is in its BETA phase
<p align="center">
<img
@@ -33,7 +33,7 @@
**The modern web setup for static sites with a sprinkle of JavaScript!**
- **Meta Framework:** Build on top of giants like <a href="https://www.11ty.dev/">eleventy</a>, <a href="https://rollupjs.org/">Rollup</a>, and <a href="https://www.modern-web.dev/">Modern Web</a>.
- **Meta Framework:** Build on top of giants like <a href="https://www.11ty.dev/">Eleventy</a>, <a href="https://rollupjs.org/">Rollup</a>, and <a href="https://www.modern-web.dev/">Modern Web</a>.
- **Powerful Default Template:** Provide content and you are ready to go.
- **Small:** No overblown tools or frontend frameworks, add JavaScript and/or Web Components only on pages where needed..
@@ -41,12 +41,12 @@
<a href="https://rocket.modern-web.dev/guides/"><strong>Explore the Rocket Guides&nbsp;&nbsp;▶</strong></a>
</p>
## The goal for Rocket
## The Goal for Rocket
> Our goal is to provide developers with a meta framework for static websites with a spricle of JavaScript.
> Our goal is to provide developers with a meta framework for static websites with a sprinkle of JavaScript.
Get a site up and running in no time and focus on the content.
You can still tweak every details of every underlying tool that get's used.
You can still tweak every detail of every underlying tool that gets used.
Rocket is part of the [Modern Web Family](https://twitter.com/modern_web_dev).
@@ -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

View File

@@ -1,4 +1,4 @@
---
layout: 404.njk
layout: layout-404
permalink: 404.html
---

10
docs/_assets/body.css Normal file
View File

@@ -0,0 +1,10 @@
html {
--demo-background-color: #eee;
--demo-color: #222;
}
html[theme="dark"] body {
background: #333;
--demo-background-color: #888;
--demo-color: #eee;
}

View File

@@ -1,11 +1,11 @@
body[layout='home'] .markdown-body .call-to-action:nth-of-type(2) {
body[layout^='layout-home'] .markdown-body .call-to-action:nth-of-type(2) {
--primary-color: #222;
--primary-color-lighter: #333;
--primary-color-darker: #000;
}
@media screen and (min-width: 1024px) {
body[layout='home'][home-layout='background'] .page-background {
body[layout='layout-home-background'] .page-background {
top: -210px;
right: -463px;
transform: rotate(45deg);

View File

@@ -16,7 +16,7 @@
"name": "Follow",
"children": [
{
"text": "Github",
"text": "GitHub",
"href": "https://github.com/modernweb-dev/rocket"
},
{

View File

@@ -3,7 +3,7 @@ module.exports = async function () {
dir: 'ltr',
lang: 'en',
name: 'Rocket',
description: 'Rocket is the way to build fast static websites with a sprinkle of javascript',
description: 'Rocket is the way to build fast static websites with a sprinkle of JavaScript',
socialLinks: [
{
name: 'GitHub',

View File

@@ -7,3 +7,5 @@
rel="stylesheet"
/>
<meta name="twitter:creator" content="@modern_web_dev" />
<link rel="stylesheet" href="{{ '/_assets/body.css' | asset | url }}" mdjs-use>

View File

@@ -1,5 +1,5 @@
# Slack
You can also find us on the Polymer slack in the [#open-wc](https://slack.com/share/IUQNUPWUF/awabyN8iYH4dXX6aGpu16ES9/enQtOTc2Nzc2ODEyOTY3LWM5ZGExNGFiMmM4NDY2YWI2MzYwOGY5ZTNlZjk4OGU4NTFhMGJjNmVhNGI4MzVlNTMwNGRiNGIxNjc4MGJhNDg) channel.
You can also find us on the Polymer Slack in the [#open-wc](https://polymer.slack.com/archives/CE6D9DN05) channel.
You can join the Polymer slack by visiting https://www.polymer-project.org/slack-invite.
You can join the Polymer Slack by visiting [https://www.polymer-project.org/slack-invite](https://www.polymer-project.org/slack-invite).

View File

@@ -6,4 +6,4 @@ eleventyNavigation:
We currently can only accept sponsoring in the form of services or contributions.
If you are interested in monetary sponsoring please [let as know as](mailto:hello@modern-web.dev).
If you are interested in monetary sponsoring please [let us know](mailto:hello@modern-web.dev).

View File

@@ -1,6 +1,5 @@
---
title: Rocket Blog
layout: blog
layout: layout-blog-overview
eleventyNavigation:
key: Blog
order: 30
@@ -11,4 +10,6 @@ pagination:
alias: posts
---
# Rocket Blog
Discover articles from the core team and contributors about Rocket, tips and tricks included!

View File

@@ -0,0 +1,12 @@
const { createSocialImage } = require('@rocket/cli');
module.exports = async function () {
const socialMediaImage = await createSocialImage({
title: 'Introducing',
subTitle: 'check-html-links',
footer: 'Rocket Blog',
});
return {
socialMediaImage,
};
};

View File

@@ -0,0 +1,206 @@
---
title: Introducing Check HTMl Links - no more bad links
published: true
description: A fast link checker for static HTML
tags: [html, javascript, webdev, node]
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/an9z6f4hdll2jlne43u3.jpg
---
**TL;DR : I created a standalone tool that can help you fix all the broken links in your websites/documentation. You can check it out [on npm as check-html-links](https://www.npmjs.com/package/check-html-links)**
In my developer career, I have put live multiple websites and honestly often within a few days, there was always this one issue raised. "This link on xxx is broken". 🤦‍♂️
Often these things happen as somewhere a page got moved or renamed and not every location got updated.
It's really hard to catch especially if you have a dynamic page like with WordPress or an SPA. And for users, there is nothing worse than landing on your documentation only to find a 404 staring back at them.
Luckily, with the rise of SSG (Static Site Generators), this problem becomes easier to tackle and can be solved in large part. The reason for that is that with all HTML rendered upfront as static files we can read all of them and check every link.
## Evaluation and the Decision for a New Tool
Of course, I am not the first one to come up with that idea and there are multiple tools available on the market already.
However, when checking existing solutions I found out that most of them didn't satisfy me in at least one way 😅. Things I noticed: slow to execute, deprecated, large dependency tree, confusing output for the user, ...
Reviewing these tools I decided to create my own, with the following requirements :
- Blazing fast
- User-focused output
- Few dependencies, to keep it lean
- Preferably in the Node.js ecosystem
## Focusing on Useful Output
Most tools evaluated check files individually and report on their findings individually. That means if you have a broken link in your header or footer, you will get one line (or even multiple lines) of an error message(s) for EVERY page.
I tested this on the [11ty-website](https://github.com/11ty/11ty-website) and there are currently 516 broken links in 501 files. However, **the source of those 516 broken links is just 13 missing pages/resources**.
In my implementation, I decided to switch from an "Error in File Focused" method to a "Missing File Focused". Let's see this with examples
### Error in File Focused
This is what a lot of current existing solutions implement. Here is part of the output that is being produced:
```
[...]
authors/ryzokuken/index.html
target does not exist --- authors/ryzokuken/index.html --> /speedlify/
authors/alex_kaul/index.html
target does not exist --- authors/alex_kaul/index.html --> /speedlify/
docs/config/index.html
target does not exist --- docs/config/index.html --> /speedlify/
hash does not exist --- docs/config/index.html --> /docs/copy/#disabling-passthrough-file-copy
authors/cramforce/index.html
target does not exist --- authors/cramforce/index.html --> /speedlify/
authors/accudio/index.html
target does not exist --- authors/accudio/index.html --> /speedlify/
[...]
```
We get ~2000 lines of errors for `/speedlify/` as it's not found ~500 times. In the middle of those errors, we also see some other broken links.
Because the reporting is focusing first on the files, and then on the actual error **it is difficult to know where most errors originate from**.
### Missing File Focused
Let us turn that around and focus on missing references indeed. Here is the output for the same input website :
```
[...]
1. missing reference target _site/speedlify/index.html
from _site/404.html:1942:13 via href="/speedlify/"
from _site/authors/_amorgunov/index.html:2031:13 via href="/speedlify/"
from _site/authors/_coolcut/index.html:2031:13 via href="/speedlify/"
... 495 more references to this target
2. missing id="disabling-passthrough-file-copy" in _site/docs/copy/index.html
from _site/docs/config/index.html:2527:267 via href="/docs/copy/#disabling-passthrough-file-copy"
3. missing reference target _site/authors/dkruythoff/github.com/dkruythoff/darius-codes
from _site/authors/dkruythoff/index.html:2102:234 via href="github.com/dkruythoff/darius-codes"
[...]
```
We get one 5 line error for `/speedlify/` and it tells us it's missing 495 times + 3 examples usages.
Afterward, we find very clearly more missing references and where they occurred.
### A Clear Winner
Comparing those two outputs makes it pretty clear to me that `Missing File Focused` will make more sense if there is a chance that some links will be broken everywhere. My implementation focuses on missing links in its output. This is crucial because it allows developers to know where to focus their efforts first to get the biggest wins.
## Focusing on Speed
Speed is always nice to have but in this case, it's probably vital. I need this to be fast so that I can run it potentially on every save. Speed is also very important in case the tool runs in a CI for example. For projects with extensive documentation, we don't want to hog the CI only to check for documentation.
Luckily HTML is an awesome language to analyze as it's declarative, which means you can read and analyze it at the same time. This may even mean that the HTML is already processed by the time the file is done reading.
With this knowledge I was hopeful - but reality didn't deliver 😅. The only tool that could keep up with the speed I needed was implemented in [Go](https://golang.org/).
It seems that most tools use sophisticated parsers meant to create full syntax trees of your HTML.
In reality for link checking all you need to know are the _id_ and the _href_ attributes.
I have been using [sax-wasm](https://github.com/justinwilaby/sax-wasm) in a few situations before and I knew it supported streaming. I knew that way it could be FAST 🤞!
How fast are we talking about though?
As a rule of thumb, I decided that the analysis should be finished within 1s for a small site (up to 200 pages).
The main reason is already listed above: To not disturb during writing/development as it will run on every save.
For medium sites (200 - 1000 pages), it's reasonable if it takes a little longer - let's aim for less than 5 seconds. This will probably be a breaking point where you execute it only on-demand and in the CI instead of executing it on every save.
Results are gatherd on January 26, 2021:
| Website | Pages | Duration |
| ----------- | ----- | -------- |
| open-wc.org | 90 | ~0.4s |
| 11ty.dev | 501 | ~2.5s |
| web.dev | 830 | ~3.7s |
| eslint.org | 3475 | ~12.4s |
## Being Part of the Node.js Ecosystem
My daily workflow is hugely dominated by JavaScript, so it was only natural to want to stay in the same environment if I could reach my earlier requirements with it.
On top of this, the end goal is to integrate it within a bigger WIP system called [Rocket](https://github.com/modernweb-dev/rocket) which is node-based so therefore it will need to at least support Node.js. Having it standalone (usable via `npx`) also makes it more versatile and easier to maintain/test.
## Focusing on a Small Dependency Tree
The JavaScript and Node.js ecosystem is very active and constantly shifting. Lots of changes/improvements happen all the time. It's often hard to keep up. Therefore having a small dependency tree is something to always thrive for because it will reduce the maintenance burden down the line. And as an added benefit, it makes it smaller and easily embeddable as less stuff has to go down the wire. Lean is king 👑.
## Solution
As already mentioned I went on and implement a link checker myself 😅. So far it fits all my requirements so I call it a success 🎉! You can find it [on npm](https://www.npmjs.com/package/check-html-links).
I called it `check-html-links` and its slogan is "no more broken links or assets".
The features so far are:
- extracts every attribute value of id, href, src, srcset
- use a Wasm parser (sax-wasm)
- streams the HTML for performance
- check if file or id within file exist
- focus on missing references/sources
## Usage
It checks your final HTML output so you need to execute it after your Static Site Generator.
```
npx check-html-links _site
```
## GitHub Action Usage
[Julien](https://twitter.com/jlengrand) created a GitHub action available for the tool, so you can easily plug it in your existing CI. You can find it [on the GitHub Marketplace](https://github.com/marketplace/actions/check-html-links-action).
Here is a complete example workflow that will check the result of the folder `_site` in the root of your repository on each push:
```yml
on: [push]
jobs:
check_html_links_job:
runs-on: ubuntu-latest
name: A job to test check-html-links-action
steps:
- uses: actions/checkout@v2
- name: check-html-links-action step
id: check-links
uses: modernweb-dev/check-html-links-action@v1
with:
doc-folder: '_site_'
```
## Comparison
Checking the output of the [11ty-website](https://github.com/11ty/11ty-website) with 13 missing reference targets (used by 516 links) while checking 501 files. (on January 17, 2021)
| Tool | Lines printed | Duration | Lang | Dependency Tree |
| ---------------- | ------------- | -------- | ---- | --------------- |
| check-html-links | 38 | ~2.5s | node | 19 |
| link-checker | 3000+ | ~11s | node | 106 |
| hyperlink | 68 | 4m 20s | node | 481 |
| htmltest | 1000+ | ~0.7s | GO | - |
## Future
The basic functionality is finished and it's reasonabley fast.
Topic to work on:
- Allow to ignore folders (potentially via a cli parameter)
- Support for `<base href="/">`
- Big Sites Speed improvements (potentially running multiple parsers in parallel for 1000+ pages)
- Speed improvements by introducing a "permanent cache" for the parse result (if file did not change, parse result will not change - we still check all links)
- Memory consumption check (see if there is room for improvements)
- Improve node api
- Check external links
## Acknowledgements
Thank you for following along on my journey on creating `check-html-links`. You can find the code on [GitHub](https://github.com/modernweb-dev/rocket/tree/main/packages/check-html-links).
Follow us on [Twitter](https://twitter.com/modern_web_dev), or follow me on my personal [Twitter](https://twitter.com/dakmor).
Thanks to [Julien](https://twitter.com/jlengrand) for feedback and helping turn my scribbles to a followable story.
If you think my open source work is valuable then I would like you to check out my personal [GitHub Sponsor Page](https://github.com/sponsors/daKmoR). Or you can support our whole group via the [Modern Web Open Collective](https://opencollective.com/modern-web).
---
<span>Photo by <a href="https://unsplash.com/@mihaiteslariu0?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Teslariu Mihai</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,16 +0,0 @@
---
title: Introducing rocket - effective static content with some javascript
published: true
description: Write Interactive Demos Using Markdown and JavaScript
tags: [markdown, javascript, webcomponents, demos]
cover_image: /blog/introducing-rocket/images/blog-header.jpg
socialMediaImage: /blog/introducing-rocket/images/social-media-image.jpg
---
Welcome to the next level of content creation.
## Here comes the navigation
Stuff
## Another anchor

View File

@@ -0,0 +1,42 @@
# Configuration >> Computed Config ||20
If you want to add data that depends on other data then you can do it via [Eleventy's computed data](https://www.11ty.dev/docs/data-computed/).
Rocket exposes it via `setupEleventyComputedConfig`.
## Set Your Own Data
Let's say you want to add a `Welcome to the contact page` everywhere. (A filter might be a better choice, but it's a good example of the concept.)
👉 `rocket.config.mjs` (or your theme config file)
```js
import { addPlugin } from 'plugins-manager';
/** @type {Partial<import("../../../types/main").RocketCliOptions>} */
const config = {
setupEleventyComputedConfig: [
addPlugin({ name: 'greeting', plugin: data => `Welcome to the ${data.title} page.` }),
],
};
export default config;
```
{% raw %}
Now you can use {{ greeting }} everywhere.
{% endraw %}
And it will be correctly replaced with a Welcome and the page title.
## Default Available Configs
```js
[
{ name: 'titleMeta', plugin: titleMetaPlugin },
{ name: 'title', plugin: titlePlugin },
{ name: 'eleventyNavigation', plugin: eleventyNavigationPlugin },
{ name: 'section', plugin: sectionPlugin },
{ name: 'socialMediaImage', plugin: socialMediaImagePlugin },
{ name: 'templateBlocks', plugin: templateBlocksPlugin, options: rocketConfig },
];
```

View File

@@ -2,32 +2,43 @@
The configuration file is `rocket.config.js` or `rocket.config.mjs`.
The config files consists of the following parts:
The config files consist of the following parts:
```js
import { rocketLaunch } from '@rocket/launch';
export default {
presets: [rocketLaunch()],
build: {
emptyOutputDir: true,
pathPrefix: 'subfolder-only-for-build',
serviceWorkerFileName: 'service-worker.js',
},
};
```
Rocket is primarily build around plugins for each of it's systems.
Rocket is primarily build around plugins for each of its systems.
New plugins can be added and all default plugins can be adjusted or even removed by using the following functions.
```js
export default {
// add remark/unified plugin to the Markdown processing (e.g. enable special code blocks)
setupUnifiedPlugins: [],
// add a rollup plugins to the web dev server (will be wrapped with @web/dev-server-rollup) AND the rollup build (e.g. enable json importing)
setupDevAndBuildPlugins: [],
// add a plugin to the web dev server (will not be wrapped) (e.g. esbuild for TypeScript)
setupDevPlugins: [],
// add a plugin to the rollup build (e.g. optimization steps)
setupBuildPlugins: [],
// add a plugin to Eleventy (e.g. a filter packs)
setupEleventyPlugins: [],
// add a computedConfig to Eleventy (e.g. site wide default variables like socialMediaImage)
setupEleventyComputedConfig: [],
// add a plugin to the cli (e.g. a new command like "rocket my-command")
setupCliPlugins: [],
};
```
@@ -40,9 +51,9 @@ For some projects you might want to enable non-standard behaviors like importing
import data from './data.json';
```
You can accomplish this with rollup and dev server plugins. Make sure to add both the dev-server plugin as well as the rollup plugin, so that the behaviors is the same during development as it is in the production build.
You can accomplish this with Rollup and dev server plugins. Make sure to add both the dev-server plugin as well as the Rollup plugin, so that the behaviors is the same during development as it is in the production build.
For these cases you can use `setupDevAndBuildPlugins`, which will automatically add the plugin for you to both rollup and dev-server:
For these cases you can use `setupDevAndBuildPlugins`, which will automatically add the plugin for you to both Rollup and dev-server:
```js
import json from '@rollup/plugin-json';
@@ -58,8 +69,8 @@ const config = {
export default config;
```
This will add the rollup plugin `json` with the id `json` at the top of the plugin list of rollup and the dev server. It needs to be at the top so further plugins down the line can work with json imports.
For the Dev Server the plugins are automatically wrapped by `@web/dev-server-rollup`. Note that [not all rollup plugins](https://modern-web.dev/docs/dev-server/plugins/rollup/#compatibility-with-rollup-plugins) will work with the dev-server.
This will add the Rollup plugin `json` with the id `json` at the top of the plugin list of Rollup and the dev server. It needs to be at the top so further plugins down the line can work with JSON imports.
For the Dev Server the plugins are automatically wrapped by `@web/dev-server-rollup`. Note that [not all Rollup plugins](https://modern-web.dev/docs/dev-server/plugins/rollup/#compatibility-with-rollup-plugins) will work with the dev-server.
## Modifying Options of Plugins

View File

@@ -1,6 +1,6 @@
# Eleventy Plugins >> Markdown JavaScript (Mdjs)
# Eleventy Plugins >> Markdown JavaScript (mdjs)
Use mdjs in your 11ty site.
Use mdjs in your Eleventy site.
## Setup
@@ -8,7 +8,7 @@ Use mdjs in your 11ty site.
npm install @rocket/eleventy-plugin-mdjs
```
Create an 11ty config file `.eleventy.js`
Create an Eleventy config file `.eleventy.js`
```js
const pluginMdjs = require('@rocket/eleventy-plugin-mdjs');
@@ -18,43 +18,11 @@ module.exports = function (eleventyConfig) {
};
```
As mdjs does return html AND javascript at the same time we need to have a template that can understand it. For that we create a layout file.
👉 `_includes/layout.njk`
{% raw %}
```js
<main>
{{ content.html | safe }}
</main>
<script type="module">
{{ content.jsCode | safe }}
</script>
```
{% endraw %}
And in our content we then need to make sure to use that template.
👉 `index.md`
```
---
layout: layout.njk
---
# Hello World
```
You can see a minimal setup in the [examples repo](https://github.com/daKmoR/rocket-example-projects/tree/master/eleventy-and-mdjs).
## Configure a unified or remark plugin with mdjs
## Configure a unified or remark Plugin with mdjs
By providing a `setupUnifiedPlugins` function as an option to `eleventy-plugin-mdjs` you can set options for all unified/remark plugins.
We do use [plugins-manager](../plugins-manager/overview.md).
We do use [plugins-manager](../tools/plugins-manager.md).
This example adds a CSS class to the `htmlHeading` plugin so heading links can be selected in CSS.
@@ -75,12 +43,12 @@ module.exports = function (eleventyConfig) {
};
```
## Add a unified or remark plugin
## Add a unified or remark Plugin
The order of plugins is important in unified as each plugin processes the content and passes on its result.
Some plugins do work with the markdown AST and some with the rehype (e.g. html) AST. In order to get access to the correct AST the plugin needs to be in a specific location in the processing order.
Some plugins do work with the Markdown AST and some with the rehype (e.g. HTML) AST. In order to get access to the correct AST the plugin needs to be in a specific location in the processing order.
Examples on how to insert a plugin right after creating the markdown AST.
Examples on how to insert a plugin right after creating the Markdown AST.
```js
const pluginMdjs = require('@rocket/eleventy-plugin-mdjs');

View File

@@ -0,0 +1,69 @@
class DemoElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.platform = 'the web';
this.language = 'en-US';
this.theme = 'light';
this.observer = new MutationObserver(this.updateData);
}
updateData = () => {
this.platform = document.documentElement.getAttribute('platform') || 'the web';
this.language = document.documentElement.getAttribute('data-lang') || 'en-US';
this.theme = document.documentElement.getAttribute('theme') || 'light';
this.requestUpdate();
};
connectedCallback() {
this.updateData();
this.observer.observe(document.documentElement, { attributes: true });
}
requestUpdate() {
this.shadowRoot.innerHTML = this.render();
}
render() {
return `
<style>
:host {
display: block;
background: var(--demo-background-color);
color: var(--demo-color);
padding: 10px;
}
:host[platform~="web"] {
border-bottom: 2px solid #333;
}
@media screen and (min-width: 640px) {
.about {
display: flex;
}
.about ul {
width: 50%;
}
}
</style>
<p>Hello I am DemoElement 👋</p>
<div class="about">
<ul>
<li>My purpose is to demonstrate how an element can adopt to different environments</li>
<li>I like <strong>${this.platform}</strong></li>
</ul>
<ul>
<li>My mother languages is <strong>${this.language}</strong></li>
<li>I feel very comfortable in the <strong>${this.theme}</strong></li>
</ul>
</div>
`;
}
}
customElements.define('demo-element', DemoElement);

View File

@@ -1,6 +1,6 @@
# Markdown JavaScript >> Overview || 10
Markdown JavaScript (Mdjs) is a format that allows you to use JavaScript with Markdown, to create interactive demos. It does so by "annotating" JavaScript that should be executed in Markdown.
Markdown JavaScript (mdjs) is a format that allows you to use JavaScript with Markdown, to create interactive demos. It does so by "annotating" JavaScript that should be executed in Markdown.
To annotate we use a code block with `js script`.
@@ -13,9 +13,9 @@ To annotate we use a code block with `js script`.
## Web Components
One very good use case for that can be web components.
HTML already works in markdown so all you need is to load a web components definition file.
HTML already works in Markdown so all you need is to load a web components definition file.
You could even do so within the same markdown file.
You could even do so within the same Markdown file.
````md
## This is my-card
@@ -52,27 +52,25 @@ customElements.define('my-el', MyEl);
## Demo Support (Story)
mdjs comes with some additional helpers you can choose to import via
mdjs comes with some additional helpers you can choose to import:
````md
```js script
import '@mdjs/mdjs-story/mdjs-story.js';
import '@mdjs/mdjs-preview/mdjs-preview.js';
import '@mdjs/mdjs-story/define';
import '@mdjs/mdjs-preview/define';
```
````
once loaded you can use them like so.
Once loaded you can use them like so:
````md
```js script
import '@mdjs/mdjs-story/mdjs-story.js';
import '@mdjs/mdjs-preview/mdjs-preview.js';
import '@mdjs/mdjs-story/define';
import '@mdjs/mdjs-preview/define';
```
````
once loaded you can use them like so.
### story
### Story
The code snippet will actually get executed at that place and you will have a live demo
@@ -88,7 +86,7 @@ export const JsStory = () => html` <demo-wc-card>JS Story</demo-wc-card> `;
```
````
#### full code support
#### Full Code Support
````md
```js story
@@ -101,7 +99,7 @@ export const JsStory = () => {
```
````
### preview story
### Preview Story
Will become a live demo wrapped in a container with a show code button.
@@ -120,8 +118,8 @@ export const JsPreviewStory = () => html` <demo-wc-card>JS Preview Story</demo-w
Here is a live example from [demo-wc-card](https://www.npmjs.com/package/demo-wc-card).
```js script
import '@mdjs/mdjs-story/mdjs-story.js';
import '@mdjs/mdjs-preview/mdjs-preview.js';
import '@mdjs/mdjs-story/define';
import '@mdjs/mdjs-preview/define';
import { html } from 'lit-html';
```
@@ -134,40 +132,15 @@ 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 repo.
```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.
It includes [storybook-addon-markdown-docs](https://open-wc.org/demoing/storybook-addon-markdown-docs.html) which uses mdjs under the hood.
### Chrome Extension (currently only for Github)
### Chrome Extension (currently only for GitHub)
See live demos directly inside github page, markdown files, issues, pull requests...
See live demos directly inside GitHub pages, Markdown files, issues, pull requests...
Please check out [mdjs-viewer](https://github.com/open-wc/mdjs-viewer).
@@ -179,7 +152,7 @@ mdjs offers two more "basic" integrations
#### `mdjsDocPage`
Creates a full blown html page by passing in the markdown.
Creates a full blown HTML page by passing in the Markdown.
```js
const { mdjsDocPage } = require('@mdjs/core');
@@ -197,7 +170,7 @@ const page = await mdjsDocPage(markdownString);
#### `mdjsProcess`
Pass in multiple markdown documents and you get back all the jsCode and rendered html.
Pass in multiple Markdown documents and you get back all the JavaScript code and rendered HTML.
```js
const { mdjsProcess } = require('@mdjs/core');

View File

@@ -2,18 +2,165 @@
You can showcase live running code by annotating a code block with `js preview-story`.
````md
```js preview-story
import { html } from 'lit-html';
## Features
export const foo = () => html` <p>my html</p> `;
- Shows components inside the page as they are
- You can enable “Simulation Mode” to break them out
- Simulation mode renders in an iframe to supporting media queries and isolated Simulation settings
- Simulation Settings
- Style (windows, mac, android, iOS)
- Size (small, medium, large, Galaxy S5, iPhone X, iPad …)
- Automatic Height
- Theme (light, dark)
- Language (en, nl, …)
- Settings are ”global” for all Simulators (e.g. changing one will change all)
- Settings can be remembered for other pages / return visits
```js script
import { html } from 'lit-html';
import './assets/demo-element.js';
```
## JavaScript Story
````md
```js script
import { html } from 'lit-html';
import './assets/demo-element.js';
```
```js preview-story
export const foo = () => html`<demo-element></demo-element>`;
```
````
will result in
```js preview-story
import { html } from 'lit-html';
export const foo = () => html` <p>my html</p> `;
export const foo = () => html` <demo-element></demo-element> `;
```
## HTML Story
````md
```html preview-story
<demo-element></demo-element>
```
````
will result in
```html preview-story
<demo-element></demo-element>
```
## Setup Simulation Mode
For simulation mode we need a dedicated html file that will be used as an iframe target while loading stories.
The fastest way to create such a file is to use the `layout-simulator` layout.
Create a file `docs/simulator.md` with the following content.
```md
---
layout: layout-simulator
eleventyExcludeFromCollections: true
excludeFromSearch: true
---
```
Once you have that you need to configure it for the story renderer by setting it in your `rocket.config.js`.
```js
export default {
setupUnifiedPlugins: [
adjustPluginOptions('mdjsSetupCode', {
simulationSettings: { simulatorUrl: '/simulator/' },
}),
],
};
```
<inline-notification type="tip">
You can freely choose the path for the "simulator" by creating the md file in a different folder and adjusting the path in the config.
</inline-notification>
## Simulator states
To simulate these stats that usually come from the device itself we put those infos on the document tag.
We can simulate the following settings
1. `platform`
Adopting styles and behavior depending on which device platform you are.
```html
<html platform="web"></html>
<html platform="android"></html>
<html platform="ios"></html>
<!-- potentially later -->
<html platform="web-windows"></html>
<html platform="web-mac"></html>
```
2. `theme`
Adjust your styles based on a theme - light/dark are the default but you can add as many as you want.
```html
<html theme="light"></html>
<html theme="dark"></html>
```
3. `language`
Best to relay on `data-lang` and `lang` often gets changes by translations services which may interfere with you translation loading system.
```html
<html lang="en-US" data-lang="en-US"></html>
<html lang="de-DE" data-lang="de-DE"></html>
```
If you want to react to such document changes you can use an [MutationObserver](https://developer.mozilla.org/de/docs/Web/API/MutationObserver).
For a vanilla web component it could look something like this:
```js
class DemoElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.platform = 'the web';
this.language = 'en-US';
this.theme = 'light';
this.observer = new MutationObserver(this.updateData);
}
updateData = () => {
this.platform = document.documentElement.getAttribute('platform') || 'the web';
this.language = document.documentElement.getAttribute('data-lang') || 'en-US';
this.theme = document.documentElement.getAttribute('theme') || 'light';
this.requestUpdate();
};
connectedCallback() {
this.updateData();
this.observer.observe(document.documentElement, { attributes: true });
}
requestUpdate() {
this.shadowRoot.innerHTML = this.render();
}
render() {
return `
...
`;
}
}
customElements.define('demo-element', DemoElement);
```
```js script
import '@rocket/launch/inline-notification/inline-notification.js';
```

View File

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

View File

@@ -1,6 +1,6 @@
# Presets >> Blog ||30
# Presets >> Blog ||40
Enable writing blog posts within your rocket site
Enable writing blog posts within your Rocket site.
## Installation

View File

@@ -0,0 +1,95 @@
# Presets >> Joining Blocks ||10
The template system allows for a very granular control of how individual parts will be merged, overwritten or reorderd.
As a preset you may want to add this to your layout.
{% raw %}
```
<footer id="main-footer">
{% for blockName, blockPath in _joiningBlocks.footer %}
{% include blockPath %}
{% endfor %}
</footer>
```
{% endraw %}
This will now render all templates within `_includes/_joiningBlocks/footer/*`.
## Adding content without overriding
Let's assume we have a preset with the following files
```html
<!-- usedPreset/_includes/_joiningBlocks/footer/10-first.njk -->
<p>first</p>
<!-- usedPreset/_includes/_joiningBlocks/footer/20-second.njk -->
<p>second</p>
```
And it produces this in your website
```html
<footer>
<p>first</p>
<p>second</p>
</footer>
```
Now we can add a file which will insert content without needing to overwrite any of the preset file.
```html
<!-- docs/_includes/_joiningBlocks/footer/15-in-between.njk -->
<p>in-between</p>
```
the final output will be
```html
<footer>
<p>first</p>
<p>in-between</p>
<p>second</p>
</footer>
```
## Overriding Content
Now if you want to overwrite you can use the same filename.
```html
<!-- docs/_includes/_joiningBlocks/footer/10-first.njk -->
<p>updated first</p>
```
the final output will be
```html
<footer>
<p>updated first</p>
<p>second</p>
</footer>
```
## Reordering and Overriding
Sometimes you wanna reorder when you overwrite as well
```html
<!-- docs/_includes/_joiningBlocks/footer/30-first.njk -->
<p>first</p>
```
the final output will be
```html
<footer>
<p>second</p>
<p>first</p>
</footer>
```
Note: Reordering always requires you to overwrite as well.

View File

@@ -1,6 +1,6 @@
# Presets >> Launch ||10
# Presets >> Launch ||20
Rocket comes with a preset you will love. Simple, Responsive and behaving like native it sure is going to be a hit among your users.
Rocket comes with a preset you will love. Simple, responsive and behaving like native, it sure is going to be a hit among your users.
## Installation
@@ -30,7 +30,7 @@ The footer data comes from `footer.json`
Notification are web components that bring in some styles.
To use them in Markdown you need to write the html tag and have it separated by an empty line.
To use them in Markdown you need to write the HTML tag and have it separated by an empty line.
```js script
import '@rocket/launch/inline-notification/inline-notification.js';
@@ -62,7 +62,7 @@ I am a tip
</inline-notification>
```
### Modify the title
### Modify the Title
To override the title you can provide a property success.

View File

@@ -1,4 +1,4 @@
# Presets >> Search ||20
# Presets >> Search ||30
Add a search for all your static content.

View File

@@ -0,0 +1,63 @@
# Tools >> Check HTML Links ||30
```js
import '@rocket/launch/inline-notification/inline-notification.js';
```
A fast checker for broken links/references in HTML.
<inline-notification type="tip">
Read the [Introducing Check HTMl Links - no more bad links](../../blog/introducing-check-html-links.md) Blog post to find out how it came to be and how it works.
</inline-notification>
## Features
- Checks all html files for broken local links/references (in href, src, srcset)
- Focuses on the broken reference targets and groups references to it
- Fast (can process 500-1000 documents in ~2-3 seconds)
- Has only 3 dependencies (and 19 in the full tree)
- Uses [sax-wasm](https://github.com/justinwilaby/sax-wasm) for parsing streamed HTML
## Installation
```
npm i -D check-html-links
```
## CLI flags
| Name | Type | Description |
| ------------------- | ------- | --------------------------------------------------------------------------------------------------- |
| root-dir | string | the root directory to serve files from. Defaults to the current working directory |
| ignore-link-pattern | string | do not check links matching the pattern |
| continue-on-error | boolean | if present it will not exit with an error code - useful while writing or for temporary passing a ci |
## Usage Examples
```bash
# check a folder _site
npx check-html-links _site
# ignore all links like <a href="/users/123">
npx check-html-links _site --ignore-link-pattern "/users/*" "/users/**/*"
# ignore all links like <a href="/users/123"> & <a href="/users/123/details">
npx check-html-links _site --ignore-link-pattern "/users/*" "/users/**/*"
```
## Example Output
![Test Run Screenshot](./images/check-html-links-screenshot.png)
## Comparison
Checking the output of [11ty-website](https://github.com/11ty/11ty-website) with 13 missing reference targets (used by 516 links) while checking 501 files. (on January 17, 2021)
| Tool | Lines printed | Times | Lang | Dependency Tree |
| ---------------- | ------------- | ------ | ---- | --------------- |
| check-html-links | 38 | ~2.5s | node | 19 |
| link-checker | 3000+ | ~11s | node | 106 |
| hyperlink | 68 | 4m 20s | node | 481 |
| htmltest | 1000+ | ~0.7s | GO | - |

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

View File

@@ -34,7 +34,7 @@ var processor = unified().use(markdown).use(toc, { maxDepth: 2 });
// ...
```
## Problem statement
## Problem Statement
> Executing or adding a plugin in a special way is a one time process. You can not transparently later change the options of the given plugin.
@@ -42,7 +42,7 @@ This means if you wish to define default plugins and allow your user to override
## Solution
The plugins manager let's you orchestrate a set of "meta plugins" which are defined by
The plugins manager lets you orchestrate a set of "meta plugins" which are defined by
- name
- plugin
@@ -193,7 +193,7 @@ adjustPluginOptions('json', false);
// resulting options = false
```
## Converting metaPlugins to an actual plugin
## Converting metaPlugins to an Actual Plugin
To execute all setup function you can use this little helper
@@ -216,7 +216,11 @@ const plugins = finalMetaPlugins.map(pluginObj => {
**Examples**
Rollup has a more specific helper
Rollup has a more specific helper that handles
- `config.setupPlugins`
Note: if you provide `config.plugins` then it will return that directly ignoring `setupPlugins`
```js
import { metaConfigToRollupConfig } from 'plugins-manager';
@@ -224,14 +228,19 @@ import { metaConfigToRollupConfig } from 'plugins-manager';
const finalConfig = metaConfigToRollupConfig(currentConfig, defaultMetaPlugins);
```
Web Dev Server has a more specific helper
Web Dev Server has a more specific helper that handles
- `config.setupPlugins`
- `config.setupRollupPlugins`
Note: if you provide `config.plugins` then it will return that directly ignoring `setupPlugins` and `setupRollupPlugins`
```js
import { metaConfigToWebDevServerConfig } from 'plugins-manager';
import { fromRollup } from '@web/dev-server-rollup';
const finalConfig = metaConfigToWebDevServerConfig(currentConfig, defaultMetaPlugins, {
wrapperFunction: fromRollup,
rollupWrapperFunction: fromRollup,
});
```

View File

@@ -1,15 +1,15 @@
# Tools >> Rollup Config
# Tools >> Rollup Config ||20
Rollup configuration to help you get started building modern web applications.
You write modern javascript using the latest browser-features, rollup will optimize your code for production and ensure it runs on all supported browsers.
You write modern JavaScript using the latest browser features. Rollup will optimize your code for production and ensure it runs on all supported browsers.
## Features
- Set HTML or JS as input and/or output
- Set HTML or JavaScript as input and/or output
- Optimized for browsers which support modules
- Loads polyfills using feature detection
- Generates a service worker
- Minifies JS
- Minifies JavaScript
- Minifies lit-html templates
## Setup
@@ -25,7 +25,7 @@ You write modern javascript using the latest browser-features, rollup will optim
```js
import { createSpaConfig } from '@rocket/building-rollup';
// use `import { createBasicConfig }` to do regular JS to JS bundling
// use `import { createMpaConfig }` to bundle multiple html files
// use `import { createMpaConfig }` to bundle multiple HTML files
export default createSpaConfig({
input: 'index.html',
@@ -52,9 +52,9 @@ You write modern javascript using the latest browser-features, rollup will optim
Our config sets you up with good defaults for most projects. Additionally you can add more plugins and adjust predefined plugins or even remove them if needed.
We use the [plugins-manager](../plugins-manager/overview.md) for it.
We use the [plugins-manager](./plugins-manager.md) for it.
### Customizing the babel config
### Customizing the Babel Config
You can define custom babel plugins to be loaded by adding a `.babelrc` or `babel.config.js` to your project. See [babeljs config](https://babeljs.io/docs/en/configuration) for more information.
@@ -66,9 +66,9 @@ For example to add support for class properties:
}
```
### Customizing default plugins
### Customizing Default Plugins
Our config creators install a number of rollup plugins by default:
Our config creators install a number of Rollup plugins by default:
Basic, SPA and MPA plugins:
@@ -83,7 +83,7 @@ SPA and MPA plugins:
- [polyfills-loader](https://modern-web.dev/docs/building/rollup-plugin-polyfills-loader/)
- [workbox](https://www.npmjs.com/package/rollup-plugin-workbox)
You can customize options for these plugins by using [adjustPluginOptions](../plugins-manager/overview.md#adjusting-plugin-options).
You can customize options for these plugins by using [adjustPluginOptions](./plugins-manager.md#adjusting-plugin-options).
```js
import { createSpaConfig } from '@rocket/building-rollup';

View File

@@ -12,16 +12,15 @@ import { absoluteBaseUrlNetlify } from '@rocket/core/helpers';
export default /** @type {Partial<import('@rocket/cli').RocketCliOptions>} */ ({
presets: [rocketLaunch(), rocketBlog(), rocketSearch()],
emptyOutputDir: false,
absoluteBaseUrl: absoluteBaseUrlNetlify('http://localhost:8080'),
});
```
The Plugins Manager helps you register and execute your plugins across the various Rocket components - rollup, dev-server, eleventy, and markdown. It replaces the specific registration/execution call in a given plugin system by an intent to use that plugin.
The Plugins Manager helps you register and execute your plugins across the various Rocket components - Rollup, Web Dev Server, Eleventy, and Markdown. It replaces the specific registration/execution call in a given plugin system by an intent to use that plugin.
## Adding Remark/Unified Plugins
If you want to a plugin to the markdown processing you can use `setupUnifiedPlugins`.
If you want to a plugin to the Markdown processing you can use `setupUnifiedPlugins`.
```js
import emoji from 'remark-emoji';
@@ -35,5 +34,5 @@ const config = {
export default config;
```
For plugins that should handle the markdown <abbr title="Abstract Syntax Tree">AST</abbr> you should use `addPlugin({ location: 'markdown', name: 'my-plugin', plugin: MyPlugin})`. <br>
While for the rehype ast you should use `addPlugin({ location: 'remark2rehype', name: 'my-plugin', plugin: MyPlugin})`.
For plugins that should handle the Markdown <abbr title="Abstract Syntax Tree">AST</abbr> you should use `addPlugin({ location: 'markdown', name: 'my-plugin', plugin: MyPlugin})`. <br>
While for the rehype AST you should use `addPlugin({ location: 'remark2rehype', name: 'my-plugin', plugin: MyPlugin})`.

View File

@@ -8,9 +8,9 @@ It can help to examine each new page and menu carefully, to come to terms with t
</inline-notification>
## Add a section
## Add a Section
In most cases you will have multiple sections in your website and each of those sections will come with it's own sidebar navigation.
In most cases you will have multiple sections in your website and each of those sections will come with its own sidebar navigation.
To create a section you need to create a folder with an `index.md`.
@@ -38,7 +38,7 @@ Don't worry if this isn't how you would have styled or placed your menu bar or s
It might be more practical to stay below 5 sections.
## Adding a category
## Adding a Category
Often each section will have multiple categories.
@@ -54,7 +54,7 @@ mkdir docs/guides/first-pages/
# First Pages
```
## Adding a page to a category
## Adding a Page to a Category
👉 `docs/guides/first-pages/getting-started.md`
@@ -64,14 +64,14 @@ mkdir docs/guides/first-pages/
This is how you get started.
```
## Headings as anchor and menu items
## Headings as Anchor and Menu Items
_**Within**_ any page, you can still add links to your navigation!
Note that md text prefixed with one or two # signs also becomes and anchor in the page and a link in the sidebar navigation when the page is open.
Note that Markdown text prefixed with one or two # signs also becomes an anchor in the page and a link in the sidebar navigation when the page is open.
```md
## Headings as anchor and menu items
## Headings as Anchor and Menu Items
_**Within**_ any page, you can still add links to your navigation!
```
@@ -80,6 +80,6 @@ _**Within**_ any page, you can still add links to your navigation!
import '@rocket/launch/inline-notification/inline-notification.js';
```
## Example as a reference
## Example as a Reference
If implicit navigation, derived from content, is a bit too much to grasp in one sitting, feel free to examine the **docs** folder in [the rocket codebase behind the pages you are reading](https://github.com/modernweb-dev/rocket) for more examples.

View File

@@ -1,6 +1,6 @@
# First Pages >> Getting Started ||10
Rocket is has the following prerequisits:
Rocket has the following prerequisites:
- [Node 14+](https://nodejs.org/en/)
@@ -37,7 +37,7 @@ The fastest way to get started is by using an existing preset like the launch pr
You may be tempted to skip the step above, because you're not ready to commit to git yet!
Rocket uses the .gitignore file to manage it's requirements. If you skip this step, rocket will fail to deploy!
Rocket uses the .gitignore file to manage its requirements. If you skip this step, Rocket will fail to deploy!
</inline-notification>
@@ -51,20 +51,29 @@ Rocket uses the .gitignore file to manage it's requirements. If you skip this st
};
```
5. (optionally) Create a file `.eleventyignore` (this file will be needed once you start customizing presets)
```
node_modules/**
/docs/_assets
/docs/_includes
/docs/_data
```
<inline-notification type="warning" title="note">
All further pathes are relative to your project root (my-project in this case)
All further paths are relative to your project root (my-project in this case).
</inline-notification>
## Add your first page
## Add your First Page
👉 `docs/index.md`
```md
# Welcome to your Rocket site
# Welcome to Your Rocket Site
Text here, like any markdown file.
Text here, like any Markdown file.
```
This tutorial assumes you are familiar with Markdown, for page authoring.

View File

@@ -0,0 +1,29 @@
# First Pages >> Layouts ||60
The following templates are always available:
- `layout-raw` No html or any wrapping (use it for xml, json, ... outputs)
- `layout-default` For content
- `layout-index` Extends content and adds an "Open Navigation" button for mobile
Layout Default has the following Joining Blocks:
- `head` For the html `<head>`
- `header` Within the top `<header>`
- `content` Html within the main content section
- `footer` Within to bottom `<footer>`
- `bottom` Add the end of the body
## Launch Preset
On top of the above it adds the following templates
- `layout-404` A space not found page
- `layout-home` Frontpage with center logo below text
- `layout-home-background` Frontpage with left text and background image on the right
- `layout-sidebar` Left sidebar, right content
- `layout-index` Extends layout-sidebar
And the following changes
- Sets `layout-sidebar` as the default layout

View File

@@ -1,6 +1,6 @@
# First Pages >> Linking ||20
Standard markdown applies and you can link via
Standard Markdown applies. You can link like this:
```md
[visible label](./path/to/other-file.md)

View File

@@ -21,7 +21,7 @@ Will be ordered as `First`, `Second`,
## How it works
Internally `# Foo >> Bar >> Baz ||20` get's converted to.
Internally `# Foo >> Bar >> Baz ||20` gets converted to.
```
---
@@ -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.

View File

@@ -1,5 +1,5 @@
# First Pages >> Urls ||100
# First Pages >> URLs ||50
Urls will be represented by the folder structure...
URLs will be represented by the folder structure.
You can use a frontmatter with permalink to override
You can use front matter with a [permalink](https://www.11ty.dev/docs/permalinks/) to override.

View File

@@ -1,6 +1,6 @@
# First Pages >> Use JavaScript ||40
You can use `js script` to execute javascript (type = module)
You can use `js script` to execute JavaScript (`type="module"`).
````
```js script
@@ -8,7 +8,7 @@ console.log('foo');
```
````
This can be useful for importing web components and using them in markdown
This can be useful for importing web components and using them in Markdown.
````
```js script
@@ -16,7 +16,7 @@ import 'magic-reveal/magic-reveal.js';
<magic-reveal>
This text will get magically revealed. I can **still** use markdown as long as between the opening/closing tag there is an empty line.
This text will get magically revealed. I can **still** use Markdown as long as there is an empty line between the opening/closing tags and my text.
</magic-reveal>
```

View File

@@ -1,30 +1,30 @@
# Go Live >> Overview
# Go Live >> Overview ||10
A few things are usually needed before going live "for real".
## Add a not found page
## Add a Not Found Page
When a user enters a url that does not exists then a "famous" 404 page not found error occures.
When a user enters a URL that does not exist, a "famous" 404 Page Not Found error occurs.
Many servers are configured to handle this automatically and to serve a 404.html page instead.
The [Rocket Lauch Preset](../../docs/presets/launch.md) ships a default 404 template you can use.
The [Rocket Launch preset](../../docs/presets/launch.md) ships a default 404 template you can use.
To enable it you need to creating a 404.md and use the 404 layout.
To enable it, you need to create a 404.md and use the 404 layout.
👉 `docs/404.md`
```
---
layout: 404.njk
layout: layout-404
permalink: 404.html
---
```
## Add a sitemap
## Add a Sitemap
A sitemap can be used to inform search engines or services about the pages your site has.
You can create one by adding this file
You can create one by adding this file:
👉 `docs/sitemap.njk`
@@ -32,7 +32,7 @@ You can create one by adding this file
```
---
layout: pure-content.njk
layout: layout-raw
permalink: /sitemap.xml
eleventyExcludeFromCollections: true
---

View File

@@ -0,0 +1,153 @@
# Go Live >> Social Media ||20
Having a nice preview image for social media can be very helpful.
For that reason Rocket creates those automatically with the title, parent title, section and your logo.
It will look like this but with your logo:
<img src="{{ socialMediaImage | url }}" width="1200" height="630" alt="Social Media Image of this page" style="border: 1px solid #000" />
There are multiple ways you can modify it.
Note: If your logo has an `<?xml>` tag it will throw an error as it will be inlined into this SVG and nested XML tags are not allowed.
## Setting it via Front Matter
You can create your own image and link it with something like this
```
---
socialMediaImage: path/to/my/image.png
---
```
## Providing Your Own Text
Sometimes extracting the title + title of parent is not enough but you still want to use the "default image".
You can create an `11tydata.cjs` file next to your page. If your page is `docs/guides/overview.md` then you create a `docs/guides/overview.11tydata.cjs`.
In there you can use the default `createSocialImage` but provide your own values.
```js
const { createSocialImage } = require('@rocket/cli');
module.exports = async function () {
const socialMediaImage = await createSocialImage({
title: 'Learning Rocket',
subTitle: 'Have a website',
subTitle2: 'in 5 Minutes',
footer: 'Rocket Guides',
// you can also override the svg only for this page by providing
// createSocialImageSvg: async () => '<svg>...</svg>'
});
return {
socialMediaImage,
};
};
```
## Override the Default Image
Often you want to have a unique style for your social media images.
For that you can provide your own function which returns a string of an SVG to render the image.
👉 `rocket.config.mjs`
```js
import { adjustPluginOptions } from 'plugins-manager';
/** @type {Partial<import("@rocket/cli").RocketCliOptions>} */
const config = {
setupEleventyComputedConfig: [
adjustPluginOptions('socialMediaImage', {
createSocialImageSvg: async ({
title = '',
subTitle = '',
subTitle2 = '',
footer = '',
logo = '',
}) => {
let svgStr = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 630" style="fill: #ecedef;">
<defs/>
<rect width="100%" height="100%" fill="#38393e"/>
<g transform="matrix(0.45, 0, 0, 0.45, 300, 60)">${logo}</g>
<g style="
font-size: 70px;
text-anchor: middle;
font-family: 'Bitstream Vera Sans','Helvetica',sans-serif;
font-weight: 700;
">
<text x="50%" y="470">
${title}
</text>
<text x="50%" y="520" style="font-size: 30px;">
${subTitle}
</text>
</g>
<text x="10" y="620" style="font-size: 30px; fill: gray;">
${footer}
</text>
</svg>
`;
return svgStr;
},
}),
],
};
export default config;
```
## Using an SVG File as a src with Nunjucks
If you have multiple variations it may be easier to save them as SVG files and use a template system.
WARNING: Untested example
👉 `rocket.config.mjs`
{% raw %}
```js
import { adjustPluginOptions } from 'plugins-manager';
/** @type {Partial<import("@rocket/cli").RocketCliOptions>} */
const config = {
setupEleventyComputedConfig: [
adjustPluginOptions('socialMediaImage', {
createSocialImageSvg: async (args = {}) => {
// inside of the svg you can use {{ title }}
const svgBuffer = await fs.promises.readFile('/path/to/your/svg/file');
const svg = logoBuffer.toString();
return nunjucks.renderString(svg, args);
},
}),
],
};
{% endraw %}
```
## Enabling / Disabling
Generating images from SVG is quite fast but it can still add that's why by default during `rocket start` there will be no social media images created.
If you with so create them also during start you can
```js
const config = {
start: {
createSocialMediaImages: true,
},
};
```
Similarly, if you never want to create social media images even during build then you can globally disable it via
```js
const config = {
createSocialMediaImages: true,
};
```

View File

@@ -0,0 +1,13 @@
const { createSocialImage } = require('@rocket/cli');
module.exports = async function () {
const socialMediaImage = await createSocialImage({
title: 'Learning Rocket',
subTitle: 'Have a website',
subTitle2: 'in 5 Minutes',
footer: 'Rocket Guides',
});
return {
socialMediaImage,
};
};

View File

@@ -1,6 +1,5 @@
---
title: Learning Rocket
description: 'foo'
eleventyNavigation:
key: Guides
order: 10

View File

@@ -1,7 +1,106 @@
# Presets >> Create your own || 90
All loaded presets will be combined but you can override each file.
A preset is a setup function and a folder including `_assets`, `_data` and `_includes` (all optional).
Take a look at `docs/_merged_includes` and override what you want to override by placing the same filename into `_includes`.
To play around with a preset you can create a folder `fire-theme`.
Also works for `_assets`, `_data` ...
You then create the setup function for it with only one property called `path` which will allow Rocket to properly resolve it.
## Create a Preset Config File
👉 `fire-theme/fireTheme.js`
```js
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
export function fireTheme() {
return {
path: path.resolve(__dirname),
};
}
```
Once you have that you can start filling in content you need.
For example a we could override the full `layout.css` by adding a it like so
👉 `fire-theme/layout.css`
```css
body {
background: hotpink;
}
```
Once you have that you can add it to your Rocket config.
NOTE: The order of presets is important, as for example in this case we take everything from `rocketLaunch` but later override via `fireTheme`.
👉 `rocket-config.js`
```js
import { rocketLaunch } from '@rocket/launch';
import { fireTheme } from 'path/to/fire-theme/fireTheme.js';
export default {
presets: [rocketLaunch(), fireTheme()],
};
```
## Publish a Preset
If you would like to publish a preset to use it on multiple websites or share it with your friends you can do like so.
1. Pick a name for the package => for this example we take `fire-theme`.
2. Create a new folder `fire-theme`
3. Create a folder `fire-theme/preset` copy `fireTheme.js` from [above](#create-a-preset-config-file) into `preset/fireTheme.js`
4. Add a 👉 `package.json`
```json
{
"name": "fire-theme",
"version": "0.3.0",
"description": "Fire Theme for Rocket",
"license": "MIT",
"type": "module",
"exports": {
".": "./index.js",
"./preset/": "./preset/"
},
"files": ["*.js", "preset"],
"keywords": ["rocket", "preset"]
}
```
5. Add a 👉 `index.js`
```js
export { fireTheme } from './preset/fireTheme.js';
```
6. Add a 👉 `README.md`
````
# FireTheme
This is a theme/preset for [Rocket](https://rocket.modern-web.dev/).
## Installation
```
npm i -D fire-theme
```
Add it to your 👉 `rocket.config.js`
```js
import { fireTheme } from 'fire-theme';
export default {
presets: [fireTheme()],
};
```
````

View File

@@ -1,4 +1,4 @@
# Presets >> Overriding presets ||20
# Presets >> Overriding ||20
All loaded presets will be combined but you can override each file.

View File

@@ -0,0 +1,59 @@
# Presets >> Using templates ||30
The template system allows for very granular control of how individual parts will be merged, overwritten, or reordered.
On top of the [Overriding](./overriding.md) you can do with the presets alone templates have another superpower and that is automatically joining of parts.
It is generally preferred to use `Joining Blocks` before overriding.
## Adding html to the html head
Often you will want to load some more fonts or an additional CSS file. You can do so by adding a file to the head Joining Block.
👉 `docs/_includes/_joiningBlocks/head/additional-styles.njk`
```html
<link rel="stylesheet" href="{{ '/_assets/additional-styles.css' | asset | url }}" />
```
This will add the html at the bottom of the head.
## Adding JavaScript to the bottom of the body
For executing a script you can use the `bottom` Joining Block.
👉 `docs/_includes/_joiningBlocks/bottom/my-script.njk`
```html
<script>
console.log('hello world');
</script>
```
If you look into `docs/_merged_includes/_joiningBlocks/bottom/` you will see a few scripts
- `10-init-navigation.njk`
- `180-service-worker-update.njk`
- `190-google-analytics.njk`
- `my-script.njk`
## Controlling the order
In the html `<head>` order is usually not that important but when adding script it does.
If you look into the dom then you see that its order matches with the file system order.
Now if you want to move your script in-between `init-nagivation` and `service-worker-update` then you can rename your file to
👉 `docs/_includes/_joiningBlocks/bottom/20-my-script.njk`
which brings the order to
- `10-init-navigation.njk`
- `20-my-script.njk`
- `180-service-worker-update.njk`
- `190-google-analytics.njk`
## More information
For more details please see the [Joining Blocks Docs](../../docs/presets/joining-blocks.md)

View File

@@ -1,3 +0,0 @@
# Presets >> Using preset templates ||30
Most presetse have specific entry files you can override...

13
docs/index.11tydata.cjs Normal file
View File

@@ -0,0 +1,13 @@
const { createSocialImage } = require('@rocket/cli');
module.exports = async function () {
const socialMediaImage = await createSocialImage({
title: 'Rocket',
subTitle: 'Static sites with',
subTitle2: 'a sprinkle of JavaScript.',
footer: 'A Modern Web Product',
});
return {
socialMediaImage,
};
};

View File

@@ -1,6 +1,6 @@
---
title: Rocket
layout: home.njk
layout: layout-home-background
slogan: The modern web setup for static sites with a sprinkle of JavaScript.
callToActionItems:
- text: Follow Guides
@@ -12,11 +12,11 @@ reasons:
- header: Small
text: No overblown tools or frontend frameworks, add JavaScript and/or Web Components only on pages where needed.
- header: Pre-Rendered
text: Statically generated content means less javascript to ship and process.
text: Statically generated content means less JavaScript to ship and process.
- header: Zero Configuration
text: Automatic code splitting, filesystem based routing, and javascript in markdown.
text: Automatic code splitting, filesystem based routing, and JavaScript in Markdown.
- header: Meta Framework
text: 'Build on top of giants like <a href="https://www.11ty.dev/">eleventy</a>, <a href="https://rollupjs.org/">Rollup</a>, and <a href="https://www.modern-web.dev/">Modern Web</a>.'
text: 'Build on top of giants like <a href="https://www.11ty.dev/">Eleventy</a>, <a href="https://rollupjs.org/">Rollup</a>, and <a href="https://www.modern-web.dev/">Modern Web</a>.'
- header: Powerful Default Template
text: Provide content and you are ready to go.
- header: Ready for Production

5
docs/simulator.md Normal file
View File

@@ -0,0 +1,5 @@
---
layout: layout-simulator
eleventyExcludeFromCollections: true
excludeFromSearch: true
---

View File

@@ -1,5 +1,5 @@
---
layout: pure-content.njk
layout: layout-raw
permalink: /sitemap.xml
eleventyExcludeFromCollections: true
---

View File

@@ -21,13 +21,15 @@
"lint": "run-p lint:*",
"lint:eslint": "eslint --ext .ts,.js,.mjs,.cjs .",
"lint:prettier": "node node_modules/prettier/bin-prettier.js \"**/*.{ts,js,mjs,cjs,md}\" --check --ignore-path .eslintignore",
"lint:rocket": "rocket lint",
"lint:types": "npm run types",
"lint:versions": "node scripts/lint-versions.js",
"postinstall": "npm run setup",
"release": "changeset publish && yarn format",
"rocket:build": "node packages/cli/src/cli.js build",
"search": "node packages/cli/src/cli.js search",
"setup": "npm run setup:ts-configs",
"setup": "npm run setup:ts-configs && npm run build:packages",
"setup:patches": "npx patch-package",
"setup:ts-configs": "node scripts/generate-ts-configs.mjs",
"start": "node packages/cli/src/cli.js start",
"test": "yarn test:node && yarn test:web",
@@ -50,22 +52,22 @@
"@types/chai": "^4.2.14",
"@types/fs-extra": "^9.0.6",
"@types/mocha": "^8.2.0",
"@types/node": "^14.14.16",
"@types/node": "^14.14.20",
"@types/sinon": "^9.0.10",
"@typescript-eslint/eslint-plugin": "^4.11.1",
"@typescript-eslint/parser": "^4.11.1",
"@web/test-runner": "^0.11.5",
"@web/test-runner-commands": "^0.3.0",
"@web/test-runner-playwright": "^0.7.0",
"@typescript-eslint/eslint-plugin": "^4.13.0",
"@typescript-eslint/parser": "^4.13.0",
"@web/test-runner": "^0.12.2",
"@web/test-runner-commands": "^0.4.0",
"@web/test-runner-playwright": "^0.8.0",
"chai": "^4.2.0",
"concurrently": "^5.3.0",
"copyfiles": "^2.4.1",
"deepmerge": "^4.2.2",
"esbuild": "^0.8.26",
"eslint": "^7.16.0",
"esbuild": "^0.8.31",
"eslint": "^7.17.0",
"eslint-config-prettier": "^7.1.0",
"hanbi": "^0.4.1",
"husky": "^4.3.6",
"husky": "^4.3.7",
"lint-staged": "^10.5.3",
"mocha": "^8.2.1",
"node-fetch": "^2.6.1",
@@ -76,9 +78,9 @@
"puppeteer": "^5.5.0",
"remark-emoji": "^2.1.0",
"rimraf": "^3.0.2",
"rollup": "^2.35.1",
"rollup": "^2.36.1",
"rollup-plugin-terser": "^7.0.2",
"sinon": "^9.2.2",
"sinon": "^9.2.3",
"ts-node": "^9.1.1",
"typescript": "^4.1.3"
},

View File

@@ -1,6 +1,31 @@
# @rocket/blog
## 0.3.0
### Minor Changes
- 8bdc326: Adopt to new layout system
BREAKING CHANGE:
- Renamed `blog` to `layout-blog-overview`
- Renamed `post` to `layout-blog-details`
## 0.2.0
### Minor Changes
- 4858271: Adjust templates for change in `@rocket/eleventy-plugin-mdjs-unified` as it now returns html directly instead of an object with html, js, stories
## 0.1.1
### Patch Changes
- Updated dependencies [a8c7173]
- plugins-manager@0.2.0
## 0.1.0
### Minor Changes
- 1971f5d: Initial Release

View File

@@ -1,5 +1,5 @@
# Blog Preset for Rocket
Add blog posts to your rocket site.
Add blog posts to your Rocket site.
For docs please see our homepage [https://rocket.modern-web.dev/docs/presets/blog/](https://rocket.modern-web.dev/docs/presets/blog/).

View File

@@ -1,6 +1,6 @@
{
"name": "@rocket/blog",
"version": "0.1.0",
"version": "0.3.0",
"publishConfig": {
"access": "public"
},
@@ -38,6 +38,6 @@
"testing"
],
"dependencies": {
"plugins-manager": "^0.1.0"
"plugins-manager": "^0.2.1"
}
}

View File

@@ -1,10 +1,10 @@
/** BLOG OVERVIEW **************************************************************************/
body[layout='blog-details'] rocket-navigation > ul > li.current::before {
body[layout='layout-blog-details'] rocket-navigation > ul > li.current::before {
display: none;
}
body[layout='blog-details'] #sidebar-nav li.anchor a:hover::before {
body[layout='layout-blog-details'] #sidebar-nav li.anchor a:hover::before {
display: none;
}
@@ -48,8 +48,7 @@ body[layout='blog-details'] #sidebar-nav li.anchor a:hover::before {
}
@media screen and (min-width: 1024px) {
body[layout='blog-details'] #sidebar {
body[layout='layout-blog-details'] #sidebar {
display: block;
}
}

View File

@@ -0,0 +1,3 @@
{% if cover_image %}
<img src="{{ cover_image | url }}" alt="">
{% endif %}

View File

@@ -0,0 +1 @@
{% include 'partials/_shared/blog-content-footer.njk' %}

View File

@@ -0,0 +1 @@
{% include 'partials/_shared/logoLink.njk' %}

View File

@@ -0,0 +1,25 @@
<rocket-navigation>
<ul>
<li class="current">
<h3>Headings</h3>
{{ collections[section] | rocketPageAnchors({ title: title }) | rocketNavToHtml({
listItemClass: "menu-item",
activeListItemClass: "current",
activeKey: eleventyNavigation.key
}) | safe }}
</li>
</ul>
<div class="sidebar-tags">
<h3>Date</h3>
<div>{{ page.date.toDateString() }}</div>
</div>
<div class="sidebar-tags">
<h3>Tags</h3>
<div class="tags">
{% for tag in tags %}
<span class="tag">{{tag}}</span>
{% endfor %}
</div>
</div>
{% include 'partials/mobile-sidebar-bottom.njk' %}
</rocket-navigation>

View File

@@ -0,0 +1 @@
{{ content | safe }}

View File

@@ -0,0 +1,20 @@
<div class="articles">
{% for post in posts %}
{% if post.data.published %}
<article>
{% if post.data.cover_image %}
<a href="{{ post.url | url }}">
<img src="{{ post.data.cover_image | url }}" alt="">
</a>
{% endif %}
<div class="content">
<h2>
<a href="{{ post.url | url }}">{{ post.data.title }}</a>
</h2>
<p>{{ post.data.description }}</p>
<a class="read" href="{{ post.url | url }}">...read more</a>
</div>
</article>
{% endif %}
{% endfor %}
</div>

View File

@@ -0,0 +1 @@
<link rel="stylesheet" href="{{ '/_assets/rocket-blog.css' | asset | url }}">

View File

@@ -1,57 +0,0 @@
{% extends 'with-index.njk' %}
{% block headContent %}
{{ super() }}
<link rel="stylesheet" href="{{ '/_assets/blog.css' | asset | url }}">
{% endblock %}
{% block bodyTag %}
<body layout="blog-details">
{% endblock bodyTag %}
{% block sidebar %}
<rocket-drawer id="sidebar">
<nav slot="content" id="sidebar-nav">
{% include 'partials/logoLink.njk' %}
<rocket-navigation>
<ul>
<li class="current">
<h3>Headings</h3>
{{ collections[section] | rocketPageAnchors({ title: title }) | rocketNavToHtml({
listItemClass: "menu-item",
activeListItemClass: "current",
activeKey: eleventyNavigation.key
}) | safe }}
</li>
</ul>
<div class="sidebar-tags">
<h3>Date</h3>
<div>{{ page.date.toDateString() }}</div>
</div>
<div class="sidebar-tags">
<h3>Tags</h3>
<div class="tags">
{% for tag in tags %}
<span class="tag">{{tag}}</span>
{% endfor %}
</div>
</div>
{% include 'partials/mobile-sidebar-bottom.njk' %}
</rocket-navigation>
</nav>
</rocket-drawer>
{% endblock sidebar %}
{% block main %}
<main class="markdown-body">
{% if cover_image %}
<img src="{{ cover_image | url }}" alt="">
{% endif %}
{% include 'partials/addTitleHeadline.njk' %}
{{ content.html | safe }}
{% include 'partials/previousNext.njk' %}
{% include 'partials/blog-content-footer.njk' %}
</main>
{% endblock main %}

View File

@@ -1,37 +0,0 @@
{% extends 'home.njk' %}
{% block headContent %}
{{ super() }}
<link rel="stylesheet" href="{{ '/_assets/blog.css' | asset | url }}">
{% endblock %}
{% block bodyTag %}
<body layout="blog-overview">
{% endblock bodyTag %}
{% block main %}
<main class="markdown-body">
{% include 'partials/addTitleHeadline.njk' %}
{{ content.html | safe }}
<div class="articles">
{% for post in posts %}
{% if post.data.published %}
<article>
{% if post.data.cover_image %}
<a href="{{ post.url | url }}">
<img src="{{ post.data.cover_image | url }}" alt="">
</a>
{% endif %}
<div class="content">
<h2>
<a href="{{ post.url | url }}">{{ post.data.title }}</a>
</h2>
<p>{{ post.data.description }}</p>
<a class="read" href="{{ post.url | url }}">...read more</a>
</div>
</article>
{% endif %}
{% endfor %}
</div>
</main>
{% endblock main %}

View File

@@ -0,0 +1,11 @@
{% extends 'layout-sidebar.njk' %}
{% block sidebar %}
{% include 'partials/_layoutBlogDetails/sidebar.njk' %}
{% endblock sidebar %}
{% block content %}
<main class="markdown-body">
{% include 'partials/_layoutBlogDetails/content.njk' %}
</main>
{% endblock content %}

View File

@@ -0,0 +1,5 @@
{% extends 'layout-home.njk' %}
{% block content %}
{% include 'partials/_layoutBlogOverview/content.njk' %}
{% endblock content %}

View File

@@ -0,0 +1,5 @@
<main class="markdown-body">
{% for blockName, blockPath in _joiningBlocks._layoutBlogDetails.content %}
{% include blockPath %}
{% endfor %}
</main>

View File

@@ -0,0 +1,7 @@
<rocket-drawer id="sidebar">
<nav slot="content" id="sidebar-nav">
{% for blockName, blockPath in _joiningBlocks._layoutBlogDetails.sidebar %}
{% include blockPath %}
{% endfor %}
</nav>
</rocket-drawer>

View File

@@ -0,0 +1,5 @@
<main class="markdown-body">
{% for blockName, blockPath in _joiningBlocks._layoutBlogOverview.content %}
{% include blockPath %}
{% endfor %}
</main>

View File

@@ -13,9 +13,6 @@ export function rocketBlog({ section = SECTION, postCollection = POST_COLLECTION
const eleventyPluginRocketBlog = {
configFunction: eleventyConfig => {
eleventyConfig.addLayoutAlias('blog', 'blog-overview.njk');
eleventyConfig.addLayoutAlias('post', 'blog-details.njk');
eleventyConfig.addCollection('posts', collection => {
/*
// It's not working beacuse it's a paginated collection.
@@ -41,7 +38,7 @@ export function rocketBlog({ section = SECTION, postCollection = POST_COLLECTION
const posts = eleventyConfig.collections[section](collection);
posts.forEach(page => {
page.data.layout = 'post';
page.data.layout = 'layout-blog-details';
});
return posts;
});

View File

@@ -1,6 +1,31 @@
# @rocket/building-rollup
## 0.2.0
### Minor Changes
- bad4686: Preserve attributes on script tags. Preserve export names.
## 0.1.3
### Patch Changes
- be0d0b3: fix: add missing main entry to the packages
## 0.1.2
### Patch Changes
- 897892d: bump dependencies
## 0.1.1
### Patch Changes
- 3468ff9: Update rollup-plugin-html to support `absolutePathPrefix` option
## 0.1.0
### Minor Changes
- 1971f5d: Initial Release

View File

@@ -1,6 +1,6 @@
# Building Rollup
Rollup configuration to help you get started building modern web applications.
You write modern javascript using the latest browser-features, rollup will optimize your code for production and ensure it runs on all supported browsers.
You write modern JavaScript using the latest browser features. Rollup will optimize your code for production and ensure it runs on all supported browsers.
For docs please see our homepage [https://rocket.modern-web.dev/docs/building/rollup-config/](https://rocket.modern-web.dev/docs/building/rollup-config/).

View File

@@ -2,7 +2,7 @@
import { LitElement, html, css } from 'lit-element';
import './demo-component.js';
// partial css trips up the minifier
// partial CSS trips up the minifier
const fontSize = css`
16
`;

View File

@@ -1,6 +1,6 @@
import { LitElement, html, customElement } from 'lit-element';
const msg: string = 'Typescript demo works';
const msg: string = 'TypeScript demo works';
@customElement('demo-app')
class DemoApp extends LitElement {

View File

@@ -1,6 +1,6 @@
{
"name": "@rocket/building-rollup",
"version": "0.1.0",
"version": "0.2.0",
"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/tools/building-rollup/",
"main": "./index.js",
"type": "module",
"exports": {
".": "./index.js"
@@ -54,10 +55,10 @@
"@babel/preset-env": "^7.12.11",
"@rollup/plugin-babel": "^5.2.2",
"@rollup/plugin-node-resolve": "^11.0.1",
"@web/rollup-plugin-html": "^1.3.3",
"@web/rollup-plugin-html": "^1.6.0",
"@web/rollup-plugin-import-meta-assets": "^1.0.4",
"@web/rollup-plugin-polyfills-loader": "^1.0.3",
"browserslist": "^4.16.0",
"@web/rollup-plugin-polyfills-loader": "^1.1.0",
"browserslist": "^4.16.1",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-workbox": "^6.1.0"
}

View File

@@ -22,7 +22,7 @@ export function createBasicMetaConfig(userConfig = { output: {} }) {
const assetName = `[${developmentMode ? 'name' : 'hash'}][extname]`;
const config = {
preserveEntrySignatures: false,
preserveEntrySignatures: 'strict',
treeshake: !developmentMode,
setupPlugins: [],
...userConfig,

View File

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

View File

@@ -59,13 +59,13 @@ describe('createMapConfig', () => {
stripToBody: true,
stripServiceWorker: true,
});
expect(indexHtml).to.equal('<h1>Only static html content in index.html</h1>');
expect(indexHtml).to.equal('<h1>Only static HTML content in index.html</h1>');
const subHtmlIndexHtml = await readOutput('sub-html/index.html', {
stripToBody: true,
stripServiceWorker: true,
});
expect(subHtmlIndexHtml).to.equal('<h1>Only static html content in sub-html/index.html</h1>');
expect(subHtmlIndexHtml).to.equal('<h1>Only static HTML content in sub-html/index.html</h1>');
const subJsIndexHtml = await readOutput('sub-js/index.html', {
stripToBody: true,
@@ -75,6 +75,14 @@ describe('createMapConfig', () => {
'<h1>Has js in sub-js/index.html</h1>\n\n\n<script type="module" src="../sub-js.js"></script>',
);
const subJsAbsoluteIndexHtml = await readOutput('sub-js-absolute/index.html', {
stripToBody: true,
stripServiceWorker: true,
});
expect(subJsAbsoluteIndexHtml).to.equal(
'<h1>Has js in sub-js-absolute/index.html</h1>\n\n\n<script type="module" src="../sub-js-absolute.js"></script>',
);
const serviceWorkerJs = await readOutput('service-worker.js');
expect(serviceWorkerJs).to.include('Promise'); // not empty string might be enough...
});

View File

@@ -1 +1 @@
<h1>Only static html content in index.html</h1>
<h1>Only static HTML content in index.html</h1>

View File

@@ -1 +1 @@
<h1>Only static html content in sub-html/index.html</h1>
<h1>Only static HTML content in sub-html/index.html</h1>

View File

@@ -0,0 +1,2 @@
<h1>Has js in sub-js-absolute/index.html</h1>
<script type="module" src="/sub-js-absolute/sub-js-absolute.js"></script>

View File

@@ -0,0 +1,45 @@
# check-html-links
## 0.2.2
### Patch Changes
- 66c2d78: fix: windows path issue
## 0.2.1
### Patch Changes
- be0d0b3: fix: add missing main entry to the packages
## 0.2.0
### Minor Changes
- 750418b: Uses a class for the CLI and adding the following options:
- `--root-dir` the root directory to serve files from. Defaults to the current working directory
- `--ignore-link-pattern` do not check links matching the pattern
- `--continue-on-error` if present it will not exit with an error code - useful while writing or for temporary passing a ci
BREAKING CHANGE:
- Exists with an error code if a broken link is found
## 0.1.2
### Patch Changes
- f343c50: When reading bigger files, especially bigger files with all content on one line it could mean a read chunk is in the middle of a character. This can lead to strange symbols in the resulting string. The `hightWaterMark` is now increased from the node default of 16KB to 256KB. Additionally, the `hightWaterMark` is now synced for reading and parsing.
## 0.1.1
### Patch Changes
- eb74110: Add info about how many files and links will be checked
## 0.1.0
### Minor Changes
- cd22231: Initial release

View File

@@ -0,0 +1,28 @@
# Check HTML Links
A fast checker for broken links/references in HTML.
## Installation
```
npm i -D check-html-links
```
## Usage
```
npx check-html-links _site
```
For docs please see our homepage [https://rocket.modern-web.dev/docs/tools/check-html-links/](https://rocket.modern-web.dev/docs/tools/check-html-links/).
## Comparison
Checking the output of the [11ty-website](https://github.com/11ty/11ty-website) with 13 missing reference targets (used by 516 links) while checking 501 files. (on January 17, 2021)
| Tool | Lines printed | Times | Lang | Dependency Tree |
| ---------------- | ------------- | ------ | ---- | --------------- |
| check-html-links | 38 | ~2.5s | node | 19 |
| link-checker | 3000+ | ~11s | node | 106 |
| hyperlink | 68 | 4m 20s | node | 481 |
| htmltest | 1000+ | ~0.7s | GO | - |

View File

@@ -0,0 +1,3 @@
export { validateFolder } from './src/validateFolder.js';
export { formatErrors } from './src/formatErrors.js';
export { CheckHtmlLinksCli } from './src/CheckHtmlLinksCli.js';

View File

@@ -0,0 +1,46 @@
{
"name": "check-html-links",
"version": "0.2.2",
"publishConfig": {
"access": "public"
},
"description": "A fast low dependency checker of html links/references",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/modernweb-dev/rocket.git",
"directory": "packages/check-html-links"
},
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
"homepage": "https://rocket.modern-web.dev/docs/tools/check-html-links/",
"main": "./index.js",
"bin": {
"check-html-links": "src/cli.js"
},
"type": "module",
"exports": {
".": "./index.js"
},
"scripts": {
"test": "mocha --timeout 5000 test-node/**/*.test.{js,cjs} test-node/*.test.{js,cjs}",
"test:watch": "onchange 'src/**/*.{js,cjs}' 'test-node/**/*.{js,cjs}' -- npm test",
"types:copy": "copyfiles \"./types/**/*.d.ts\" dist-types/"
},
"files": [
"*.js",
"dist",
"dist-types",
"src"
],
"dependencies": {
"chalk": "^4.0.0",
"command-line-args": "^5.1.1",
"glob": "^7.0.0",
"minimatch": "^3.0.4",
"sax-wasm": "^2.0.0"
},
"devDependencies": {
"@types/glob": "^7.0.0"
},
"types": "dist-types/index.d.ts"
}

View File

@@ -0,0 +1,100 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
/** @typedef {import('../types/main').CheckHtmlLinksCliOptions} CheckHtmlLinksCliOptions */
import path from 'path';
import chalk from 'chalk';
import commandLineArgs from 'command-line-args';
import { validateFiles } from './validateFolder.js';
import { formatErrors } from './formatErrors.js';
import { listFiles } from './listFiles.js';
export class CheckHtmlLinksCli {
/** @type {CheckHtmlLinksCliOptions} */
options;
constructor({ argv } = { argv: undefined }) {
const mainDefinitions = [
{ name: 'ignore-link-pattern', type: String, multiple: true },
{ name: 'root-dir', type: String, defaultOption: true },
{ name: 'continue-on-error', type: Boolean, defaultOption: false },
];
const options = commandLineArgs(mainDefinitions, {
stopAtFirstUnknown: true,
argv,
});
this.options = {
printOnError: true,
continueOnError: options['continue-on-error'],
rootDir: options['root-dir'],
ignoreLinkPatterns: options['ignore-link-pattern'],
};
}
/**
* @param {Partial<CheckHtmlLinksCliOptions>} newOptions
*/
setOptions(newOptions) {
this.options = {
...this.options,
...newOptions,
};
}
async run() {
const { ignoreLinkPatterns, rootDir: userRootDir } = this.options;
const rootDir = userRootDir ? path.resolve(userRootDir) : process.cwd();
const performanceStart = process.hrtime();
console.log('👀 Checking if all internal links work...');
const files = await listFiles('**/*.html', rootDir);
const filesOutput =
files.length == 0
? '🧐 No files to check. Did you select the correct folder?'
: `🔥 Found a total of ${chalk.green.bold(files.length)} files to check!`;
console.log(filesOutput);
const { errors, numberLinks } = await validateFiles(files, rootDir, { ignoreLinkPatterns });
console.log(`🔗 Found a total of ${chalk.green.bold(numberLinks)} links to validate!\n`);
const performance = process.hrtime(performanceStart);
/** @type {string[]} */
let output = [];
let message = '';
if (errors.length > 0) {
let referenceCount = 0;
for (const error of errors) {
referenceCount += error.usage.length;
}
output = [
`❌ Found ${chalk.red.bold(
errors.length.toString(),
)} missing reference targets (used by ${referenceCount} links) while checking ${
files.length
} files:`,
...formatErrors(errors)
.split('\n')
.map(line => ` ${line}`),
`Checking links duration: ${performance[0]}s ${performance[1] / 1000000}ms`,
];
message = output.join('\n');
if (this.options.printOnError === true) {
console.error(message);
}
if (this.options.continueOnError === false) {
process.exit(1);
}
} else {
console.log(
`✅ All internal links are valid. (executed in ${performance[0]}s ${
performance[1] / 1000000
}ms)`,
);
}
return { errors, message };
}
}

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env node
import { CheckHtmlLinksCli } from './CheckHtmlLinksCli.js';
const cli = new CheckHtmlLinksCli();
cli.run();

View File

@@ -0,0 +1,46 @@
import path from 'path';
import chalk from 'chalk';
/** @typedef {import('../types/main').Error} Error */
/**
* @param {Error[]} errors
* @param {*} relativeFrom
*/
export function formatErrors(errors, relativeFrom = process.cwd()) {
let output = [];
let number = 0;
for (const error of errors) {
number += 1;
const filePath = path.relative(relativeFrom, error.filePath);
if (error.onlyAnchorMissing === true) {
output.push(
`${number}. missing ${chalk.red.bold(
`id="${error.usage[0].anchor}"`,
)} in ${chalk.cyanBright(filePath)}`,
);
} else {
const firstAttribute = error.usage[0].attribute;
const title =
firstAttribute === 'src' || firstAttribute === 'srcset' ? 'file' : 'reference target';
output.push(`${number}. missing ${title} ${chalk.red.bold(filePath)}`);
}
const usageLength = error.usage.length;
for (let i = 0; i < 3 && i < usageLength; i += 1) {
const usage = error.usage[i];
const usagePath = path.relative(relativeFrom, usage.file);
const clickAbleLink = chalk.cyanBright(`${usagePath}:${usage.line + 1}:${usage.character}`);
const attributeStart = chalk.gray(`${usage.attribute}="`);
const attributeEnd = chalk.gray('"');
output.push(` from ${clickAbleLink} via ${attributeStart}${usage.value}${attributeEnd}`);
}
if (usageLength > 3) {
const more = chalk.red((usageLength - 3).toString());
output.push(` ... ${more} more references to this target`);
}
output.push('');
}
return output.join('\n');
}

View File

@@ -0,0 +1,35 @@
import fs from 'fs';
import path from 'path';
import glob from 'glob';
/**
* Lists all files using the specified glob, starting from the given root directory.
*
* Will return all matching file paths fully resolved.
*
* @param {string} fromGlob
* @param {string} rootDir
*/
export function listFiles(fromGlob, rootDir) {
return new Promise(resolve => {
glob(
fromGlob,
{ cwd: rootDir },
/**
* @param {Error | null} er
* @param {string[]} files
*/
(er, files) => {
// remember, each filepath returned is relative to rootDir
resolve(
files
// fully resolve the filename relative to rootDir
.map(filePath => path.resolve(rootDir, filePath))
// filter out directories
.filter(filePath => !fs.lstatSync(filePath).isDirectory()),
);
},
);
});
}

View File

@@ -0,0 +1,309 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import fs from 'fs';
import saxWasm from 'sax-wasm';
import minimatch from 'minimatch';
import { createRequire } from 'module';
import { listFiles } from './listFiles.js';
import path from 'path';
import slash from 'slash';
/** @typedef {import('../types/main').Link} Link */
/** @typedef {import('../types/main').LocalFile} LocalFile */
/** @typedef {import('../types/main').Usage} Usage */
/** @typedef {import('../types/main').Error} Error */
/** @typedef {import('../types/main').Options} Options */
/** @typedef {import('sax-wasm').Attribute} Attribute */
const require = createRequire(import.meta.url);
const { SaxEventType, SAXParser } = saxWasm;
const streamOptions = { highWaterMark: 256 * 1024 };
const saxPath = require.resolve('sax-wasm/lib/sax-wasm.wasm');
const saxWasmBuffer = fs.readFileSync(saxPath);
const parserReferences = new SAXParser(SaxEventType.Attribute, streamOptions);
const parserIds = new SAXParser(SaxEventType.Attribute, streamOptions);
/** @type {Error[]} */
let checkLocalFiles = [];
/** @type {Error[]} */
let errors = [];
/** @type {Map<string, string[]>} */
let idCache = new Map();
/**
* @param {string} htmlFilePath
*/
function extractReferences(htmlFilePath) {
/** @type {Link[]} */
const links = [];
/** @type {string[]} */
const ids = [];
parserReferences.eventHandler = (ev, _data) => {
if (ev === SaxEventType.Attribute) {
const data = /** @type {Attribute} */ (/** @type {any} */ (_data));
const attributeName = data.name.toString();
const value = slash(data.value.toString());
const entry = {
attribute: attributeName,
value,
htmlFilePath,
...data.value.start,
};
if (attributeName === 'href' || attributeName === 'src') {
links.push(entry);
}
if (attributeName === 'srcset') {
if (value.includes(',')) {
const srcsets = value.split(',').map(el => el.trim());
for (const srcset of srcsets) {
if (srcset.includes(' ')) {
const srcsetParts = srcset.split(' ');
links.push({ ...entry, value: srcsetParts[0] });
} else {
links.push({ ...entry, value: srcset });
}
}
} else if (value.includes(' ')) {
const srcsetParts = value.split(' ');
links.push({ ...entry, value: srcsetParts[0] });
} else {
links.push(entry);
}
}
if (attributeName === 'id') {
ids.push(value);
}
}
};
return new Promise(resolve => {
const readable = fs.createReadStream(htmlFilePath, streamOptions);
readable.on('data', chunk => {
// @ts-expect-error
parserReferences.write(chunk);
});
readable.on('end', () => {
parserReferences.end();
idCache.set(htmlFilePath, ids);
resolve({ links });
});
});
}
/**
* @param {string} filePath
* @param {string} id
*/
function idExists(filePath, id) {
if (idCache.has(filePath)) {
const cachedIds = idCache.get(filePath);
// return cachedIds.includes(id);
return new Promise(resolve => resolve(cachedIds?.includes(id)));
}
/** @type {string[]} */
const ids = [];
parserIds.eventHandler = (ev, _data) => {
const data = /** @type {Attribute} */ (/** @type {any} */ (_data));
if (ev === SaxEventType.Attribute) {
if (data.name.toString() === 'id') {
ids.push(data.value.toString());
}
}
};
return new Promise(resolve => {
const readable = fs.createReadStream(filePath, streamOptions);
readable.on('data', chunk => {
// @ts-expect-error
parserIds.write(chunk);
});
readable.on('end', () => {
parserIds.end();
idCache.set(filePath, ids);
resolve(ids.includes(id));
});
});
}
/**
* @param {string} filePath
* @param {string} anchor
* @param {Usage} usageObj
*/
function addLocalFile(filePath, anchor, usageObj) {
const foundIndex = checkLocalFiles.findIndex(item => {
return item.filePath === filePath;
});
if (foundIndex === -1) {
checkLocalFiles.push({
filePath,
onlyAnchorMissing: false,
usage: [usageObj],
});
} else {
checkLocalFiles[foundIndex].usage.push(usageObj);
}
}
/**
* @param {string} inValue
*/
function getValueAndAnchor(inValue) {
let value = inValue.replace(/&#/g, '--__check-html-links__--');
let anchor = '';
if (value.includes('#')) {
[value, anchor] = value.split('#');
}
if (value.includes('?')) {
value = value.split('?')[0];
}
if (anchor.includes(':~:')) {
anchor = anchor.split(':~:')[0];
}
if (value.includes(':~:')) {
value = value.split(':~:')[0];
}
value = value.replace(/--__check-html-links__--/g, '&#');
anchor = anchor.replace(/--__check-html-links__--/g, '&#');
value = value.trim();
anchor = anchor.trim();
return {
value,
anchor,
};
}
/**
*
* @param {Link[]} links
* @param {object} options
* @param {string} options.htmlFilePath
* @param {string} options.rootDir
* @param {function(string): boolean} options.ignoreUsage
*/
async function resolveLinks(links, { htmlFilePath, rootDir, ignoreUsage }) {
for (const hrefObj of links) {
const { value, anchor } = getValueAndAnchor(hrefObj.value);
const usageObj = {
attribute: hrefObj.attribute,
value: hrefObj.value,
file: htmlFilePath,
line: hrefObj.line,
character: hrefObj.character,
anchor,
};
let valueFile = value.endsWith('/') ? path.join(value, 'index.html') : value;
if (ignoreUsage(value)) {
// ignore
} else if (value.includes('mailto:')) {
// ignore for now - could add a check to validate if the email address is valid
} else if (valueFile === '' && anchor !== '') {
addLocalFile(htmlFilePath, anchor, usageObj);
} else if (value.startsWith('//') || value.startsWith('http')) {
// TODO: handle external urls
// external url - we do not handle that (yet)
} else if (value.startsWith('/')) {
const filePath = path.join(rootDir, valueFile);
addLocalFile(filePath, anchor, usageObj);
} else if (value === '' && anchor === '') {
// no need to check it
} else {
const filePath = path.join(path.dirname(htmlFilePath), valueFile);
addLocalFile(filePath, anchor, usageObj);
}
}
return { checkLocalFiles: [...checkLocalFiles] };
}
/**
*
* @param {Error[]} checkLocalFiles
*/
async function validateLocalFiles(checkLocalFiles) {
for (const localFileObj of checkLocalFiles) {
if (
!fs.existsSync(localFileObj.filePath) ||
fs.lstatSync(localFileObj.filePath).isDirectory()
) {
errors.push(localFileObj);
} else {
for (let i = 0; i < localFileObj.usage.length; i += 1) {
const usage = localFileObj.usage[i];
if (usage.anchor === '') {
localFileObj.usage.splice(i, 1);
i -= 1;
} else {
const isValidAnchor = await idExists(localFileObj.filePath, usage.anchor);
if (isValidAnchor) {
localFileObj.usage.splice(i, 1);
i -= 1;
}
}
}
if (localFileObj.usage.length > 0) {
if (localFileObj.usage.length === 1 && localFileObj.usage[0].anchor) {
localFileObj.onlyAnchorMissing = true;
}
errors.push(localFileObj);
}
}
}
return errors;
}
/**
* @param {string[]} files
* @param {string} rootDir
* @param {Options} opts?
*/
export async function validateFiles(files, rootDir, opts) {
await parserReferences.prepareWasm(saxWasmBuffer);
await parserIds.prepareWasm(saxWasmBuffer);
errors = [];
checkLocalFiles = [];
idCache = new Map();
let numberLinks = 0;
const ignoreLinkPatternRegExps = opts
? opts.ignoreLinkPatterns?.map(pattern => minimatch.makeRe(pattern))
: null;
/** @type {function(string): boolean} */
const ignoreUsage = ignoreLinkPatternRegExps
? usage => !!ignoreLinkPatternRegExps.find(regExp => usage.match(regExp))
: () => false;
for (const htmlFilePath of files) {
const { links } = await extractReferences(htmlFilePath);
numberLinks += links.length;
await resolveLinks(links, { htmlFilePath, rootDir, ignoreUsage });
}
await validateLocalFiles(checkLocalFiles);
return { errors: errors, numberLinks: numberLinks };
}
/**
* @param {string} inRootDir
* @param {Options} opts?
*/
export async function validateFolder(inRootDir, opts) {
const rootDir = path.resolve(inRootDir);
const files = await listFiles('**/*.html', rootDir);
const { errors } = await validateFiles(files, rootDir, opts);
return errors;
}

View File

@@ -0,0 +1,5 @@
<!-- ignore known subsystems -->
<a href="/docs/"></a>
<a href="/developer/getting-started.html#js"></a>
<a href="/developer/language-guides/"></a>
<a href="/developer/javascript.html"></a>

View File

@@ -0,0 +1,11 @@
<img src="/missing.png" alt="" />
<img src="./missing.png" alt="" />
<img src="/absolute/missing.png" alt="" />
<img src="./relative/missing.png" alt="" />
<!-- valid -->
<img src="/empty.png" alt="" />
<img src="./empty.png" alt="" />
<img src="./empty.png " alt="" />
<img src=" ./empty.png " alt="" />
<img src="./empty.png?data=in&query=params" alt="" />

View File

@@ -0,0 +1,16 @@
<a href="./page.html"></a>
<a href="./page.html#first-headline"></a>
<a href="./page.html#missing-headline"></a>
<a href="./missing-page.html#missing-headline"></a>
<a href="#local"></a>
<a href="#local-missing"></a>
<p id="local"></p>
<a href="#audit-your-angular-app&#39;s-accessibility-with-codelyzer">
Audit your Angular app's accessibility with codelyzer
</a>
<h1 id="audit-your-angular-app&#39;s-accessibility-with-codelyzer">Audit your Angular app's accessibility with codelyzer</h1>
<a href="#local:~:text=put%20your%20labels%20above%20your%20inputs">Sign-in form best practices</a>

View File

@@ -0,0 +1 @@
<h1 id="first-headline">First Headline</h1>

Some files were not shown because too many files have changed in this diff Show More