mirror of
https://github.com/modernweb-dev/rocket.git
synced 2026-03-21 15:54:57 +00:00
Compare commits
40 Commits
@mdjs/mdjs
...
@rocket/en
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ca2bc6205 | ||
|
|
e53e0ebd6d | ||
|
|
87c10ec1d3 | ||
|
|
d7e461ca31 | ||
|
|
a12adf2cb5 | ||
|
|
acf84416dc | ||
|
|
a48dcd849b | ||
|
|
0ed3d6d0e9 | ||
|
|
57ec19fecc | ||
|
|
8b48fb9760 | ||
|
|
39206a1738 | ||
|
|
cbfb0f91e2 | ||
|
|
58692147e9 | ||
|
|
8dedc56afa | ||
|
|
bcbfae332d | ||
|
|
0fae0037d8 | ||
|
|
390335da18 | ||
|
|
6d2f469d26 | ||
|
|
94a6f54585 | ||
|
|
ff8b4c5cd5 | ||
|
|
5122ea8639 | ||
|
|
3032ba9b82 | ||
|
|
93503ed309 | ||
|
|
77646abbee | ||
|
|
9ae3966fef | ||
|
|
09a47b43dc | ||
|
|
42d794bdfc | ||
|
|
b8a1b45953 | ||
|
|
379f08ff47 | ||
|
|
50bb68ab7f | ||
|
|
250ca87a9d | ||
|
|
6f88d8ef6f | ||
|
|
35ed64dca1 | ||
|
|
2555a8698d | ||
|
|
367529c211 | ||
|
|
ce3298d218 | ||
|
|
a22da493dd | ||
|
|
7a8f165625 | ||
|
|
c8081071f7 | ||
|
|
ab2436162c |
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -7,5 +7,6 @@
|
||||
"search.exclude": {
|
||||
"**/*-mdjs-generated.js": true,
|
||||
"**/dist-types": true,
|
||||
}
|
||||
},
|
||||
"editor.experimental.stickyScroll.enabled": true
|
||||
}
|
||||
|
||||
10
README.md
10
README.md
@@ -2,15 +2,15 @@
|
||||
|
||||
<p align="center">
|
||||
<picture width="60%">
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/modernweb-dev/rocket/next/site/src/assets/rocket-logo-dark-with-text.svg">
|
||||
<img alt="Rocket Logo" src="https://raw.githubusercontent.com/modernweb-dev/rocket/next/site/src/assets/rocket-logo-light-with-text.svg">
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/modernweb-dev/rocket/main/site/src/assets/rocket-logo-dark-with-text.svg">
|
||||
<img alt="Rocket Logo" src="https://raw.githubusercontent.com/modernweb-dev/rocket/main/site/src/assets/rocket-logo-light-with-text.svg">
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/modernweb-dev/rocket/actions"
|
||||
><img
|
||||
src="https://img.shields.io/github/workflow/status/modernweb-dev/rocket/Release/next?label=workflow&style=flat-square"
|
||||
src="https://img.shields.io/github/workflow/status/modernweb-dev/rocket/Release/main?label=workflow&style=flat-square"
|
||||
alt="GitHub Actions workflow status"
|
||||
/></a>
|
||||
<a href="https://twitter.com/modern_web_dev"
|
||||
@@ -28,7 +28,7 @@
|
||||
<p align="center">
|
||||
<a href="https://rocket.modern-web.dev">Website</a>
|
||||
·
|
||||
<a href="https://rocket.modern-web.dev/doc/">Documentation</a>
|
||||
<a href="https://rocket.modern-web.dev/docs/">Documentation</a>
|
||||
·
|
||||
<a href="https://rocket.modern-web.dev/chat">Discord Community</a>
|
||||
</p>
|
||||
@@ -68,7 +68,7 @@ npx @rocket/create@latest
|
||||
|
||||
We are always looking for contributors of all skill levels! If you're looking to ease your way into the project, try out a [good first issue](https://github.com/modernweb-dev/rocket/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).
|
||||
|
||||
If you are interested in helping contribute to Modern Web, please take a look at our [Contributing Guide](https://github.com/modernweb-dev/rocket/blob/next/CONTRIBUTING.md). Also, feel free to drop into [discord](https://rocket.modern-web.dev/chat) and say hi. 👋
|
||||
If you are interested in helping contribute to Modern Web, please take a look at our [Contributing Guide](https://github.com/modernweb-dev/rocket/blob/main/CONTRIBUTING.md). Also, feel free to drop into [discord](https://rocket.modern-web.dev/chat) and say hi. 👋
|
||||
|
||||
### Financial Contributors
|
||||
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
"scripts": {
|
||||
"build": "rocket build",
|
||||
"dev": "npm start",
|
||||
"preview": "rocket preview",
|
||||
"preview": "rocket preview --open",
|
||||
"start": "NODE_DEBUG=engine:rendering rocket start --open"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rocket/cli": "^0.20.0",
|
||||
"@rocket/engine": "^0.2.0",
|
||||
"@webcomponents/template-shadowroot": "^0.1.0",
|
||||
"lit": "^2.2.5"
|
||||
"lit": "^2.3.0"
|
||||
},
|
||||
"@rocket/template-name": "Hydration Starter",
|
||||
"imports": {
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
"scripts": {
|
||||
"build": "rocket build",
|
||||
"dev": "npm start",
|
||||
"preview": "rocket preview",
|
||||
"preview": "rocket preview --open",
|
||||
"start": "NODE_DEBUG=engine:rendering rocket start --open"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rocket/cli": "^0.20.0",
|
||||
"@rocket/engine": "^0.2.0",
|
||||
"lit": "^2.2.5"
|
||||
"lit": "^2.3.0"
|
||||
},
|
||||
"@rocket/template-name": "Blog Starter"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"title": "Example Blog",
|
||||
"h1": "\n \n ",
|
||||
"h1": "My Blog",
|
||||
"name": "Example Blog",
|
||||
"menuLinkText": "\n \n ",
|
||||
"menuLinkText": "My Blog",
|
||||
"url": "/",
|
||||
"outputRelativeFilePath": "index.html",
|
||||
"sourceRelativeFilePath": "index.rocket.js",
|
||||
@@ -20,7 +20,7 @@
|
||||
"children": [
|
||||
{
|
||||
"title": "",
|
||||
"h1": "\n \n ",
|
||||
"h1": "My Blog",
|
||||
"headlinesWithId": [
|
||||
{
|
||||
"text": "About",
|
||||
@@ -28,8 +28,8 @@
|
||||
"level": 1
|
||||
}
|
||||
],
|
||||
"name": "\n \n ",
|
||||
"menuLinkText": "\n \n ",
|
||||
"name": "My Blog",
|
||||
"menuLinkText": "My Blog",
|
||||
"url": "/about/",
|
||||
"outputRelativeFilePath": "about/index.html",
|
||||
"sourceRelativeFilePath": "about.rocket.md",
|
||||
@@ -60,9 +60,9 @@
|
||||
"children": [
|
||||
{
|
||||
"title": "Hello world!",
|
||||
"h1": "\n \n ",
|
||||
"h1": "My Blog",
|
||||
"name": "Hello world!",
|
||||
"menuLinkText": "\n \n ",
|
||||
"menuLinkText": "My Blog",
|
||||
"url": "/blog/hello-world/",
|
||||
"outputRelativeFilePath": "blog/hello-world/index.html",
|
||||
"sourceRelativeFilePath": "blog/hello-world.rocket.md",
|
||||
@@ -82,9 +82,9 @@
|
||||
},
|
||||
{
|
||||
"title": "With Image!",
|
||||
"h1": "\n \n ",
|
||||
"h1": "My Blog",
|
||||
"name": "With Image!",
|
||||
"menuLinkText": "\n \n ",
|
||||
"menuLinkText": "My Blog",
|
||||
"url": "/blog/with-image/",
|
||||
"outputRelativeFilePath": "blog/with-image/index.html",
|
||||
"sourceRelativeFilePath": "blog/with-image.rocket.md",
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
"scripts": {
|
||||
"build": "rocket build",
|
||||
"dev": "npm start",
|
||||
"preview": "rocket preview",
|
||||
"preview": "rocket preview --open",
|
||||
"start": "NODE_DEBUG=engine:rendering rocket start --open"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rocket/cli": "^0.20.0",
|
||||
"@rocket/engine": "^0.2.0",
|
||||
"lit": "^2.2.5"
|
||||
"lit": "^2.3.0"
|
||||
},
|
||||
"@rocket/template-name": "Minimal Starter"
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"scripts": {
|
||||
"build": "rocket build",
|
||||
"dev": "rocket start --open",
|
||||
"preview": "rocket preview",
|
||||
"preview": "rocket preview --open",
|
||||
"start": "NODE_DEBUG=engine:rendering rocket start --open"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"@sanity/client": "^3.1.0",
|
||||
"@sanity/image-url": "^1.0.1",
|
||||
"dotenv": "^16.0.0",
|
||||
"lit": "^2.2.5"
|
||||
"lit": "^2.3.0"
|
||||
},
|
||||
"@rocket/template-name": "Sanity Minimal Starter"
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"scripts": {
|
||||
"build": "rocket build",
|
||||
"dev": "npm start",
|
||||
"preview": "rocket preview",
|
||||
"preview": "rocket preview --open",
|
||||
"start": "NODE_DEBUG=engine:rendering rocket start --open"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -15,7 +15,7 @@
|
||||
"@rocket/components": "^0.2.0",
|
||||
"@rocket/engine": "^0.2.0",
|
||||
"@rocket/spark": "^0.2.0",
|
||||
"lit": "^2.2.5"
|
||||
"lit": "^2.3.0"
|
||||
},
|
||||
"@rocket/template-name": "Landing Page (@rocket/spark Theme)",
|
||||
"imports": {
|
||||
|
||||
24
examples/50-landing-theme-spark/site/pages/aliens.rocket.md
Normal file
24
examples/50-landing-theme-spark/site/pages/aliens.rocket.md
Normal file
@@ -0,0 +1,24 @@
|
||||
```js server
|
||||
/* START - Rocket auto generated - do not touch */
|
||||
export const sourceRelativeFilePath = 'aliens.rocket.md';
|
||||
import { layout, components } from './recursive.data.js';
|
||||
export { layout, components };
|
||||
export async function registerCustomElements() {
|
||||
// server-only components
|
||||
// prettier-ignore
|
||||
customElements.define('rocket-content-area', await import('@rocket/components/content-area.js').then(m => m.RocketContentArea));
|
||||
// prettier-ignore
|
||||
customElements.define('rocket-header-scroll-menu', await import('@rocket/components/header-scroll-menu.js').then(m => m.RocketHeaderScrollMenu));
|
||||
// prettier-ignore
|
||||
customElements.define('rocket-columns', await import('@rocket/components/columns.js').then(m => m.RocketColumns));
|
||||
}
|
||||
/* END - Rocket auto generated - do not touch */
|
||||
|
||||
export const title = 'Aliens';
|
||||
```
|
||||
|
||||
Vastness is bearable only through love Cambrian explosion a still more glorious dawn awaits Euclid consciousness extraordinary claims require extraordinary evidence. Realm of the galaxies invent the universe culture made in the interiors of collapsing stars Drake Equation with pretty stories for which there's little good evidence. Bits of moving fluff preserve and cherish that pale blue dot shores of the cosmic ocean the ash of stellar alchemy brain is the seed of intelligence courage of our questions.
|
||||
|
||||
With pretty stories for which there's little good evidence not a sunrise but a galaxyrise rich in heavy atoms consciousness the sky calls to us rings of Uranus. Shores of the cosmic ocean shores of the cosmic ocean a very small stage in a vast cosmic arena of brilliant syntheses laws of physics muse about? Invent the universe finite but unbounded extraordinary claims require extraordinary evidence made in the interiors of collapsing stars muse about invent the universe.
|
||||
|
||||
The ash of stellar alchemy Cambrian explosion how far away kindling the energy hidden in matter cosmic ocean descended from astronomers. The carbon in our apple pies a very small stage in a vast cosmic arena brain is the seed of intelligence the only home we've ever known the carbon in our apple pies the carbon in our apple pies? Corpus callosum muse about citizens of distant epochs finite but unbounded extraordinary claims require extraordinary evidence finite but unbounded and billions upon billions upon billions upon billions upon billions upon billions upon billions.
|
||||
30
examples/50-landing-theme-spark/site/pages/humans.rocket.md
Normal file
30
examples/50-landing-theme-spark/site/pages/humans.rocket.md
Normal file
@@ -0,0 +1,30 @@
|
||||
```js server
|
||||
/* START - Rocket auto generated - do not touch */
|
||||
export const sourceRelativeFilePath = 'humans.rocket.md';
|
||||
import { layout, components } from './recursive.data.js';
|
||||
export { layout, components };
|
||||
export async function registerCustomElements() {
|
||||
// server-only components
|
||||
// prettier-ignore
|
||||
customElements.define('rocket-content-area', await import('@rocket/components/content-area.js').then(m => m.RocketContentArea));
|
||||
// prettier-ignore
|
||||
customElements.define('rocket-header-scroll-menu', await import('@rocket/components/header-scroll-menu.js').then(m => m.RocketHeaderScrollMenu));
|
||||
// prettier-ignore
|
||||
customElements.define('rocket-columns', await import('@rocket/components/columns.js').then(m => m.RocketColumns));
|
||||
}
|
||||
/* END - Rocket auto generated - do not touch */
|
||||
|
||||
export const title = 'Humans';
|
||||
```
|
||||
|
||||
Astonishment dispassionate extraterrestrial observer Drake Equation radio telescope Hypatia of brilliant syntheses. Vastness is bearable only through love Vangelis a mote of dust suspended in a sunbeam rings of Uranus vanquish the impossible rings of Uranus? Emerged into consciousness a billion trillion laws of physics emerged into consciousness bits of moving fluff as a patch of light. Sea of Tranquility something incredible is waiting to be known permanence of the stars something incredible is waiting to be known network of wormholes invent the universe.
|
||||
|
||||
Orion's sword the carbon in our apple pies tesseract made in the interiors of collapsing stars take root and flourish Jean-François Champollion. The ash of stellar alchemy rich in heavy atoms tingling of the spine invent the universe shores of the cosmic ocean star stuff harvesting star light. Bits of moving fluff courage of our questions as a patch of light preserve and cherish that pale blue dot citizens of distant epochs concept of the number one.
|
||||
|
||||
Two ghostly white figures in coveralls and helmets are softly dancing dispassionate extraterrestrial observer tesseract finite but unbounded the sky calls to us shores of the cosmic ocean? Muse about tingling of the spine stirred by starlight muse about not a sunrise but a galaxyrise venture. Concept of the number one are creatures of the cosmos something incredible is waiting to be known courage of our questions the only home we've ever known rich in heavy atoms.
|
||||
|
||||
Across the centuries light years billions upon billions bits of moving fluff permanence of the stars consciousness. Shores of the cosmic ocean realm of the galaxies vastness is bearable only through love Sea of Tranquility network of wormholes are creatures of the cosmos? Extraordinary claims require extraordinary evidence finite but unbounded made in the interiors of collapsing stars network of wormholes finite but unbounded radio telescope. Invent the universe from which we spring with pretty stories for which there's little good evidence Euclid inconspicuous motes of rock and gas something incredible is waiting to be known.
|
||||
|
||||
Birth dream of the mind's eye prime number at the edge of forever a billion trillion permanence of the stars? Vanquish the impossible two ghostly white figures in coveralls and helmets are softly dancing vastness is bearable only through love citizens of distant epochs something incredible is waiting to be known hydrogen atoms. Two ghostly white figures in coveralls and helmets are softly dancing shores of the cosmic ocean the ash of stellar alchemy Apollonius of Perga vastness is bearable only through love network of wormholes.
|
||||
|
||||
Gathered by gravity Sea of Tranquility galaxies astonishment hearts of the stars venture. Citizens of distant epochs the carbon in our apple pies encyclopaedia galactica birth brain is the seed of intelligence permanence of the stars? A still more glorious dawn awaits concept of the number one a mote of dust suspended in a sunbeam dream of the mind's eye Euclid a very small stage in a vast cosmic arena and billions upon billions upon billions upon billions upon billions upon billions upon billions.
|
||||
@@ -128,30 +128,32 @@ export const needsLoader = true;
|
||||
|
||||
<rocket-columns>
|
||||
<rocket-card>
|
||||
<h4 slot="title">No astrophysicist</h4>
|
||||
<h4 slot="title">Life</h4>
|
||||
<p>
|
||||
would deny the possibility of life. I think we're not creative enough to imagine what life
|
||||
would be like on another planet. Show me a dead alien. Better yet, show me a live one!
|
||||
As a scientist, I want to go to Mars and back to asteroids and the Moon because I'm a
|
||||
scientist. But I can tell you, I'm not so naive a scientist to think that the nation might
|
||||
not have geopolitical reasons for going into space.
|
||||
</p>
|
||||
<a slot="cta" class="cta" href="./life.rocket.html">Details</a>
|
||||
<a slot="cta" class="cta" href="./life.rocket.md">Details</a>
|
||||
</rocket-card>
|
||||
|
||||
<rocket-card>
|
||||
<h4 slot="title">No astrophysicist</h4>
|
||||
<h4 slot="title">Aliens</h4>
|
||||
<p>
|
||||
would deny the possibility of life. I think we're not creative enough to imagine what life
|
||||
would be like on another planet. Show me a dead alien. Better yet, show me a live one!
|
||||
Vastness is bearable only through love Cambrian explosion a still more glorious dawn awaits
|
||||
Euclid consciousness extraordinary claims require extraordinary evidence.
|
||||
</p>
|
||||
<a slot="cta" class="cta" href="./life.rocket.html">Details</a>
|
||||
<a slot="cta" class="cta" href="./aliens.rocket.md">Details</a>
|
||||
</rocket-card>
|
||||
|
||||
<rocket-card>
|
||||
<h4 slot="title">No astrophysicist</h4>
|
||||
<h4 slot="title">Humans</h4>
|
||||
<p>
|
||||
would deny the possibility of life. I think we're not creative enough to imagine what life
|
||||
would be like on another planet. Show me a dead alien. Better yet, show me a live one!
|
||||
Astonishment dispassionate extraterrestrial observer Drake Equation radio telescope Hypatia
|
||||
of brilliant syntheses. Vastness is bearable only through love Vangelis a mote of dust
|
||||
suspended in a sunbeam rings of Uranus vanquish the impossible rings of Uranus?
|
||||
</p>
|
||||
<a slot="cta" class="cta" href="./life.rocket.html">Details</a>
|
||||
<a slot="cta" class="cta" href="./humans.rocket.md">Details</a>
|
||||
</rocket-card>
|
||||
</rocket-columns>
|
||||
</rocket-content-area>
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
{
|
||||
"title": "Spark Rocket Example Template",
|
||||
"h1": "\n \n \n ",
|
||||
"h1": "Home",
|
||||
"headlinesWithId": [
|
||||
{
|
||||
"text": "Home",
|
||||
"id": "home",
|
||||
"level": 1,
|
||||
"rawText": "\n \n \n "
|
||||
"rawText": "@keyframes textAnimIn {\n 0% {\n transform: translate3d(0, -120%, 0);\n }\n\n 100% {\n transform: translate3d(0, 0%, 0);\n }\n }\n\n @keyframes textAnimOut {\n 0% {\n transform: translate3d(0, 0%, 0);\n }\n\n 50% {\n transform: translate3d(0, -20%, 0);\n }\n\n 100% {\n transform: translate3d(0, 120%, 0);\n }\n }\n\n p {\n position: absolute;\n top: 0;\n margin: 0;\n transform: translate3d(0, -120%, 0);\n }\n\n .anim-static {\n transform: translate3d(0, 0, 0);\n }\n\n .anim-in {\n animation: textAnimIn 0.6s 0.3s forwards;\n }\n\n .anim-out {\n animation: textAnimOut 0.6s forwards;\n }\n\n :host {\n position: block;\n position: relative;\n overflow: hidden;\n display: block;\n height: 1.5em;\n }\n div {\n display: flex;\n max-width: 960px;\n margin: 0 auto;\n }\n slot {\n display: block;\n flex: 1;\n }\n \n Welcome\n \n Hello\n \n Aloa\n lit-part-->\n to the world of science."
|
||||
},
|
||||
{
|
||||
"text": "Credit",
|
||||
"id": "credit",
|
||||
"level": 2,
|
||||
"rawText": "\n People credit me for \n \n "
|
||||
"rawText": "People credit me for \n making the universe interesting,"
|
||||
},
|
||||
{
|
||||
"text": "Knowledge",
|
||||
"id": "knowledge",
|
||||
"level": 2,
|
||||
"rawText": "\n I've known from long ago \n \n "
|
||||
"rawText": "I've known from long ago \n that the universe was calling me"
|
||||
},
|
||||
{
|
||||
"text": "Sign Up",
|
||||
@@ -30,7 +30,7 @@
|
||||
"text": "Say",
|
||||
"id": "say",
|
||||
"level": 2,
|
||||
"rawText": "\n What others \n about us?\n "
|
||||
"rawText": "What others say \n about us?"
|
||||
},
|
||||
{
|
||||
"text": "FAQ",
|
||||
@@ -39,7 +39,7 @@
|
||||
}
|
||||
],
|
||||
"name": "Spark Rocket Example Template",
|
||||
"menuLinkText": "\n \n \n ",
|
||||
"menuLinkText": "Home",
|
||||
"url": "/",
|
||||
"outputRelativeFilePath": "index.html",
|
||||
"sourceRelativeFilePath": "index.rocket.html",
|
||||
@@ -69,6 +69,70 @@
|
||||
"description": "Everyone can code a website!",
|
||||
"needsLoader": true,
|
||||
"children": [
|
||||
{
|
||||
"title": "Aliens",
|
||||
"h1": "Aliens",
|
||||
"name": "Aliens",
|
||||
"menuLinkText": "Aliens",
|
||||
"url": "/aliens/",
|
||||
"outputRelativeFilePath": "aliens/index.html",
|
||||
"sourceRelativeFilePath": "aliens.rocket.md",
|
||||
"level": 1,
|
||||
"components": {
|
||||
"rocket-columns": "@rocket/components/columns.js::RocketColumns",
|
||||
"rocket-card": "@rocket/components/card.js::RocketCard",
|
||||
"rocket-content-area": "@rocket/components/content-area.js::RocketContentArea",
|
||||
"rocket-details": "@rocket/components/details.js::RocketDetails",
|
||||
"rocket-drawer": "@rocket/components/drawer.js::RocketDrawer",
|
||||
"rocket-feature-small": "@rocket/components/feature-small.js::RocketFeatureSmall",
|
||||
"rocket-header": "@rocket/components/header.js::RocketHeader",
|
||||
"rocket-header-scroll-menu": "@rocket/components/header-scroll-menu.js::RocketHeaderScrollMenu",
|
||||
"rocket-icon": "@rocket/components/icon.js::RocketIcon",
|
||||
"rocket-icon-card": "@rocket/components/icon-card.js::RocketIconCard",
|
||||
"rocket-main": "@rocket/components/main.js::RocketMain",
|
||||
"rocket-main-docs": "@rocket/components/main-docs.js::RocketMainDocs",
|
||||
"rocket-opengraph-overview": "@rocket/components/open-graph-overview.js::RocketOpenGraphOverview",
|
||||
"rocket-rotating-text": "@rocket/components/rotating-text.js::RocketRotatingText",
|
||||
"rocket-social-link": "@rocket/components/social-link.js::RocketSocialLink",
|
||||
"rocket-testimonial-small": "@rocket/components/testimonial-small.js::RocketTestimonialSmall",
|
||||
"inline-notification": "@rocket/components/inline-notification.js::InlineNotification",
|
||||
"permanent-notification": "@rocket/components/permanent-notification.js::PermanentNotification",
|
||||
"block-blue": "@rocket/spark/block-blue.js::BlockBlue",
|
||||
"block-features": "@rocket/spark/block-features.js::BlockFeatures"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Humans",
|
||||
"h1": "Humans",
|
||||
"name": "Humans",
|
||||
"menuLinkText": "Humans",
|
||||
"url": "/humans/",
|
||||
"outputRelativeFilePath": "humans/index.html",
|
||||
"sourceRelativeFilePath": "humans.rocket.md",
|
||||
"level": 1,
|
||||
"components": {
|
||||
"rocket-columns": "@rocket/components/columns.js::RocketColumns",
|
||||
"rocket-card": "@rocket/components/card.js::RocketCard",
|
||||
"rocket-content-area": "@rocket/components/content-area.js::RocketContentArea",
|
||||
"rocket-details": "@rocket/components/details.js::RocketDetails",
|
||||
"rocket-drawer": "@rocket/components/drawer.js::RocketDrawer",
|
||||
"rocket-feature-small": "@rocket/components/feature-small.js::RocketFeatureSmall",
|
||||
"rocket-header": "@rocket/components/header.js::RocketHeader",
|
||||
"rocket-header-scroll-menu": "@rocket/components/header-scroll-menu.js::RocketHeaderScrollMenu",
|
||||
"rocket-icon": "@rocket/components/icon.js::RocketIcon",
|
||||
"rocket-icon-card": "@rocket/components/icon-card.js::RocketIconCard",
|
||||
"rocket-main": "@rocket/components/main.js::RocketMain",
|
||||
"rocket-main-docs": "@rocket/components/main-docs.js::RocketMainDocs",
|
||||
"rocket-opengraph-overview": "@rocket/components/open-graph-overview.js::RocketOpenGraphOverview",
|
||||
"rocket-rotating-text": "@rocket/components/rotating-text.js::RocketRotatingText",
|
||||
"rocket-social-link": "@rocket/components/social-link.js::RocketSocialLink",
|
||||
"rocket-testimonial-small": "@rocket/components/testimonial-small.js::RocketTestimonialSmall",
|
||||
"inline-notification": "@rocket/components/inline-notification.js::InlineNotification",
|
||||
"permanent-notification": "@rocket/components/permanent-notification.js::PermanentNotification",
|
||||
"block-blue": "@rocket/spark/block-blue.js::BlockBlue",
|
||||
"block-features": "@rocket/spark/block-features.js::BlockFeatures"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Life",
|
||||
"h1": "Life",
|
||||
|
||||
@@ -21,7 +21,7 @@ export const layoutData = {
|
||||
Modern Web<br />
|
||||
Internet 12<br />
|
||||
0000 Web<br />
|
||||
<a href="#">office@modern-web.dev</a><br />
|
||||
<a href="#">hello@modern-web.dev</a><br />
|
||||
<a href="#">0000 / 11223344</a>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"scripts": {
|
||||
"build": "rocket build",
|
||||
"dev": "npm start",
|
||||
"preview": "rocket preview",
|
||||
"preview": "rocket preview --open",
|
||||
"start": "NODE_DEBUG=engine:rendering rocket start --open"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -15,7 +15,7 @@
|
||||
"@rocket/engine": "^0.2.0",
|
||||
"@rocket/launch": "^0.21.0",
|
||||
"@rocket/search": "^0.7.0",
|
||||
"lit": "^2.2.5"
|
||||
"lit": "^2.3.0"
|
||||
},
|
||||
"@rocket/template-name": "Documentation Website (@rocket/launch Theme)",
|
||||
"imports": {
|
||||
|
||||
@@ -12,6 +12,10 @@ export async function registerCustomElements() {
|
||||
'rocket-header',
|
||||
await import('@rocket/components/header.js').then(m => m.RocketHeader),
|
||||
);
|
||||
customElements.define(
|
||||
'launch-blog-preview',
|
||||
await import('@rocket/launch/blog-preview.js').then(m => m.LaunchBlogPreview),
|
||||
);
|
||||
customElements.define(
|
||||
'launch-blog-overview',
|
||||
await import('@rocket/launch/blog-overview.js').then(m => m.LaunchBlogOverview),
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"title": "Welcome to Rocket",
|
||||
"h1": "\n \n ",
|
||||
"name": "\n \n ",
|
||||
"menuLinkText": "\n \n ",
|
||||
"h1": "",
|
||||
"name": "Welcome to Rocket",
|
||||
"menuLinkText": "Welcome to Rocket",
|
||||
"url": "/",
|
||||
"outputRelativeFilePath": "index.html",
|
||||
"sourceRelativeFilePath": "index.rocket.js",
|
||||
|
||||
@@ -30,13 +30,14 @@
|
||||
"rocket:build": "NODE_DEBUG=engine:rendering node --trace-warnings packages/cli/src/cli.js build",
|
||||
"rocket:upgrade": "node packages/cli/src/cli.js upgrade",
|
||||
"search": "node packages/cli/src/cli.js search",
|
||||
"setup": "npm run setup:ts-configs && npm run setup:patches",
|
||||
"setup": "npm run setup:ts-configs",
|
||||
"setup:patches": "npx patch-package",
|
||||
"setup:ts-configs": "node scripts/generate-ts-configs.mjs",
|
||||
"start:experimental": "NODE_DEBUG=engine:rendering node --no-warnings --experimental-loader ./packages/engine/src/litCssLoader.js packages/cli/src/cli.js start --open",
|
||||
"start": "NODE_DEBUG=engine:rendering node --trace-warnings packages/cli/src/cli.js start --open",
|
||||
"preview": "node packages/cli/src/cli.js preview --open",
|
||||
"test": "yarn test:node && yarn test:web",
|
||||
"test:integration": "playwright test packages/*/test-node/*.spec.js",
|
||||
"test:integration": "playwright test packages/*/test-node/*.spec.js --retries=3",
|
||||
"test:node": "yarn test:unit && yarn test:integration",
|
||||
"test:unit": "node --trace-warnings ./node_modules/.bin/mocha --require ./scripts/testMochaGlobalHooks.js \"packages/*/test-node/**/*.test.{ts,js,mjs,cjs}\" -- --timeout 8000 --reporter dot --exit",
|
||||
"test:web": "web-test-runner",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @rocket/building-rollup
|
||||
|
||||
## 0.4.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- a22da49: Make sure user provided `developmentMode` actually gets applied.
|
||||
|
||||
## 0.4.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@rocket/building-rollup",
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.1",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
|
||||
@@ -13,7 +13,7 @@ export function createBasicConfig(userConfig) {
|
||||
|
||||
export function createBasicMetaConfig(userConfig = { output: {} }) {
|
||||
const developmentMode =
|
||||
typeof userConfig.developmentMode !== undefined
|
||||
typeof userConfig.developmentMode !== 'undefined'
|
||||
? userConfig.developmentMode
|
||||
: !!process.env.ROLLUP_WATCH;
|
||||
delete userConfig.developmentMode;
|
||||
|
||||
@@ -14,7 +14,7 @@ export function createServiceWorkerConfig(userConfig) {
|
||||
|
||||
export function createServiceWorkerMetaConfig(userConfig = { output: {} }) {
|
||||
const developmentMode =
|
||||
typeof userConfig.developmentMode !== undefined
|
||||
typeof userConfig.developmentMode !== 'undefined'
|
||||
? userConfig.developmentMode
|
||||
: !!process.env.ROLLUP_WATCH;
|
||||
delete userConfig.developmentMode;
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"glob": "^7.0.0",
|
||||
"minimatch": "^3.0.4",
|
||||
"sax-wasm": "^2.0.0",
|
||||
"slash": "^3.0.0"
|
||||
"slash": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/glob": "^7.0.0"
|
||||
|
||||
@@ -1,5 +1,49 @@
|
||||
# @rocket/cli
|
||||
|
||||
## 0.20.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- a48dcd8: Introducing `rocket lint` to verify if all your links are correct.
|
||||
|
||||
There are two modes:
|
||||
|
||||
```bash
|
||||
# check existing production build in _site (need to execute "rocket build" before)
|
||||
rocket lint
|
||||
|
||||
# run a fast html only build and then check it
|
||||
rocket lint --build-html
|
||||
```
|
||||
|
||||
- Updated dependencies [0ed3d6d]
|
||||
- @rocket/engine@0.2.7
|
||||
|
||||
## 0.20.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 39206a1: `rocket start` now outputs to `_site-dev` instead of `_site`.
|
||||
- 39206a1: `rocket start` clears only its output folder (defaults to `_site-dev`)
|
||||
- cbfb0f9: Add `rocket preview` command to enable fast checking of the production build
|
||||
|
||||
## 0.20.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 8dedc56: Add start message for `rocket start`
|
||||
|
||||
```
|
||||
🚀 Rocket Engine v0.2.5
|
||||
|
||||
🚧 Local: http://localhost:8000/
|
||||
🌐 Network: http://xxx.xxx.xxx.xxx:8000/
|
||||
```
|
||||
|
||||
- Updated dependencies [8dedc56]
|
||||
- Updated dependencies [390335d]
|
||||
- @rocket/engine@0.2.6
|
||||
|
||||
## 0.20.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@rocket/cli",
|
||||
"version": "0.20.0",
|
||||
"version": "0.20.3",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
@@ -53,16 +53,18 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@rocket/building-rollup": "^0.4.0",
|
||||
"@rocket/engine": "^0.2.0",
|
||||
"@web/rollup-plugin-copy": "^0.3.0",
|
||||
"@rocket/engine": "^0.2.7",
|
||||
"check-html-links": "^0.2.3",
|
||||
"colorette": "^2.0.16",
|
||||
"commander": "^9.0.0",
|
||||
"fs-extra": "^9.0.1",
|
||||
"gray-matter": "^4.0.3",
|
||||
"ip": "^1.1.5",
|
||||
"plugins-manager": "^0.3.0",
|
||||
"puppeteer": "^13.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ip": "^1.1.0",
|
||||
"koa-proxy": "^1.0.0-alpha.3"
|
||||
},
|
||||
"types": "./dist-types/src/index.d.ts",
|
||||
|
||||
@@ -1,78 +1,18 @@
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
// @ts-nocheck
|
||||
|
||||
import { Engine } from '@rocket/engine/server';
|
||||
import { gatherFiles } from '@rocket/engine';
|
||||
|
||||
import { fromRollup } from '@web/dev-server-rollup';
|
||||
|
||||
import { rollup } from 'rollup';
|
||||
import path from 'path';
|
||||
import { rollupPluginHTML } from '@web/rollup-plugin-html';
|
||||
|
||||
import { createMpaConfig, createServiceWorkerConfig } from '@rocket/building-rollup';
|
||||
import { adjustPluginOptions } from 'plugins-manager';
|
||||
import { existsSync } from 'fs';
|
||||
import { readFile, unlink, writeFile } from 'fs/promises';
|
||||
import { readFile, writeFile } from 'fs/promises';
|
||||
|
||||
import puppeteer from 'puppeteer';
|
||||
|
||||
/**
|
||||
* @param {object} config
|
||||
*/
|
||||
async function buildAndWrite(config) {
|
||||
const bundle = await rollup(config);
|
||||
|
||||
if (Array.isArray(config.output)) {
|
||||
await bundle.write(config.output[0]);
|
||||
await bundle.write(config.output[1]);
|
||||
} else {
|
||||
await bundle.write(config.output);
|
||||
}
|
||||
}
|
||||
|
||||
async function productionBuild(config) {
|
||||
const defaultSetupPlugins = [];
|
||||
if (config.pathPrefix) {
|
||||
defaultSetupPlugins.push(
|
||||
adjustPluginOptions(rollupPluginHTML, { absolutePathPrefix: config.pathPrefix }),
|
||||
);
|
||||
}
|
||||
|
||||
const mpaConfig = createMpaConfig({
|
||||
input: '**/*.html',
|
||||
output: {
|
||||
dir: config.outputDir,
|
||||
},
|
||||
// custom
|
||||
rootDir: path.resolve(config.outputDevDir),
|
||||
absoluteBaseUrl: config.absoluteBaseUrl,
|
||||
setupPlugins: [
|
||||
...defaultSetupPlugins,
|
||||
...config.setupDevServerAndBuildPlugins,
|
||||
...config.setupBuildPlugins,
|
||||
],
|
||||
});
|
||||
const finalConfig =
|
||||
typeof config.adjustBuildOptions === 'function'
|
||||
? config.adjustBuildOptions(mpaConfig)
|
||||
: mpaConfig;
|
||||
await buildAndWrite(finalConfig);
|
||||
|
||||
const { serviceWorkerSourcePath } = config;
|
||||
if (existsSync(serviceWorkerSourcePath)) {
|
||||
const serviceWorkerConfig = createServiceWorkerConfig({
|
||||
input: serviceWorkerSourcePath,
|
||||
output: {
|
||||
file: path.join(path.resolve(config.outputDir), config.serviceWorkerName),
|
||||
},
|
||||
});
|
||||
|
||||
await buildAndWrite(serviceWorkerConfig);
|
||||
}
|
||||
}
|
||||
import { buildHtml } from './build/buildHtml.js';
|
||||
import { buildOpenGraphImages } from './build/buildOpenGraphImages.js';
|
||||
import { buildJavaScriptOptimizedOutput } from './build/buildJavaScriptOptimizedOutput.js';
|
||||
|
||||
export class RocketBuild {
|
||||
/**
|
||||
* @param {import('commander').Command} program
|
||||
* @param {import('./RocketCli.js').RocketCli} cli
|
||||
*/
|
||||
async setupCommand(program, cli) {
|
||||
this.cli = cli;
|
||||
|
||||
@@ -87,30 +27,30 @@ export class RocketBuild {
|
||||
}
|
||||
|
||||
async build() {
|
||||
await this.cli.events.dispatchEventDone('build-start');
|
||||
|
||||
this.engine = new Engine();
|
||||
this.engine.setOptions({
|
||||
docsDir: this.cli.options.inputDir,
|
||||
outputDir: this.cli.options.outputDevDir,
|
||||
setupPlugins: this.cli.options.setupEnginePlugins,
|
||||
longFileHeaderWidth: this.cli.options.longFileHeaderWidth,
|
||||
longFileHeaderComment: this.cli.options.longFileHeaderComment,
|
||||
renderMode: 'production',
|
||||
clearOutputDir: this.cli.options.clearOutputDir,
|
||||
});
|
||||
console.log('Engine building...');
|
||||
await this.engine.build({ autoStop: this.cli.options.buildAutoStop });
|
||||
|
||||
if (this.cli.options.buildOpenGraphImages) {
|
||||
console.log('Generating Open Graph Images...');
|
||||
await this.buildOpenGraphImages();
|
||||
if (!this.cli) {
|
||||
return;
|
||||
}
|
||||
// for typescript as `this.cli.options.outputDir` supports other inputs as well
|
||||
// but the cli will normalize it to a string before calling plugins
|
||||
if (typeof this.cli.options.outputDir !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.cli.options.buildOptimize) {
|
||||
await this.cli.events.dispatchEventDone('build-start');
|
||||
|
||||
// 1. build html
|
||||
this.engine = await buildHtml(this.cli);
|
||||
|
||||
// 2. build open graph images
|
||||
if (this.cli.options.buildOpenGraphImages) {
|
||||
console.log('Generating Open Graph Images...');
|
||||
await buildOpenGraphImages(this.cli);
|
||||
}
|
||||
|
||||
// 3. build optimized output
|
||||
if (this.cli.options.buildOptimize && this.engine) {
|
||||
console.log('Optimize Production Build...');
|
||||
await productionBuild(this.cli.options);
|
||||
await this.engine.copyPublicFilesTo(this.cli.options.outputDir);
|
||||
await buildJavaScriptOptimizedOutput(this.cli, this.engine);
|
||||
}
|
||||
|
||||
// hackfix 404.html by making all asset urls absolute (rollup always makes them relative) which will break if netlify serves the content form a different url
|
||||
@@ -128,87 +68,4 @@ export class RocketBuild {
|
||||
|
||||
await this.cli.events.dispatchEventDone('build-end');
|
||||
}
|
||||
|
||||
async buildOpenGraphImages() {
|
||||
const openGraphFiles = await gatherFiles(this.cli.options.outputDevDir, {
|
||||
fileEndings: ['.opengraph.html'],
|
||||
});
|
||||
if (openGraphFiles.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: enable URL support in the Engine and remove this "workaround"
|
||||
if (
|
||||
typeof this.cli.options.inputDir !== 'string' ||
|
||||
typeof this.cli.options.outputDevDir !== 'string'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const withWrap = this.cli.options.setupDevServerAndBuildPlugins
|
||||
? this.cli.options.setupDevServerAndBuildPlugins.map(modFunction => {
|
||||
modFunction.wrapPlugin = fromRollup;
|
||||
return modFunction;
|
||||
})
|
||||
: [];
|
||||
|
||||
this.engine = new Engine();
|
||||
this.engine.setOptions({
|
||||
docsDir: this.cli.options.inputDir,
|
||||
outputDir: this.cli.options.outputDevDir,
|
||||
setupPlugins: this.cli.options.setupEnginePlugins,
|
||||
open: false,
|
||||
clearOutputDir: false,
|
||||
adjustDevServerOptions: this.cli.options.adjustDevServerOptions,
|
||||
setupDevServerMiddleware: this.cli.options.setupDevServerMiddleware,
|
||||
setupDevServerPlugins: [...this.cli.options.setupDevServerPlugins, ...withWrap],
|
||||
});
|
||||
try {
|
||||
await this.engine.start();
|
||||
|
||||
const browser = await puppeteer.launch();
|
||||
const page = await browser.newPage();
|
||||
|
||||
// In 2022 Twitter & Facebook recommend a size of 1200x628 - we capture with 2 dpr for retina displays
|
||||
await page.setViewport({
|
||||
width: 1200,
|
||||
height: 628,
|
||||
deviceScaleFactor: 2,
|
||||
});
|
||||
|
||||
for (const openGraphFile of openGraphFiles) {
|
||||
const relUrl = path.relative(this.cli.options.outputDevDir, openGraphFile);
|
||||
const imagePath = openGraphFile.replace('.opengraph.html', '.opengraph.png');
|
||||
const htmlPath = openGraphFile.replace('.opengraph.html', '.html');
|
||||
const relImageUrl = path.basename(imagePath);
|
||||
|
||||
let htmlString = await readFile(htmlPath, 'utf8');
|
||||
if (!htmlString.includes('<meta property="og:image"')) {
|
||||
if (htmlString.includes('</head>')) {
|
||||
htmlString = htmlString.replace(
|
||||
'</head>',
|
||||
[
|
||||
' <meta property="og:image:width" content="2400">',
|
||||
' <meta property="og:image:height" content="1256">',
|
||||
` <meta property="og:image" content="./${relImageUrl}">`,
|
||||
' </head>',
|
||||
].join('\n'),
|
||||
);
|
||||
}
|
||||
}
|
||||
const url = `http://localhost:${this.engine.devServer.config.port}/${relUrl}`;
|
||||
await page.goto(url, { waitUntil: 'networkidle0' });
|
||||
await page.screenshot({ path: imagePath });
|
||||
|
||||
await unlink(openGraphFile);
|
||||
await writeFile(htmlPath, htmlString);
|
||||
}
|
||||
await browser.close();
|
||||
|
||||
await this.engine.stop();
|
||||
} catch (e) {
|
||||
console.log('Could not start dev server to generate open graph images');
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
import { Command } from 'commander';
|
||||
import { RocketStart } from './RocketStart.js';
|
||||
import { RocketBuild } from './RocketBuild.js';
|
||||
import { RocketInit } from './RocketInit.js';
|
||||
import { RocketLint } from './RocketLint.js';
|
||||
import { RocketUpgrade } from './RocketUpgrade.js';
|
||||
import { RocketPreview } from './RocketPreview.js';
|
||||
// import { ignore } from './images/ignore.js';
|
||||
|
||||
import path from 'path';
|
||||
@@ -34,7 +35,7 @@ export class RocketCli {
|
||||
open: false,
|
||||
cwd: process.cwd(),
|
||||
inputDir: 'FALLBACK',
|
||||
outputDir: '_site',
|
||||
outputDir: 'FALLBACK',
|
||||
outputDevDir: '_site-dev',
|
||||
|
||||
serviceWorkerSourcePath: '',
|
||||
@@ -53,6 +54,10 @@ export class RocketCli {
|
||||
absoluteBaseUrl: '',
|
||||
clearOutputDir: true,
|
||||
|
||||
lint: {
|
||||
buildHtml: false,
|
||||
},
|
||||
|
||||
// /** @type {{[key: string]: ImagePreset}} */
|
||||
// imagePresets: {
|
||||
// responsive: {
|
||||
@@ -93,6 +98,9 @@ export class RocketCli {
|
||||
if (this.options.inputDir === 'FALLBACK') {
|
||||
this.options.inputDir = path.join(this.options.cwd, 'site', 'pages');
|
||||
}
|
||||
if (this.options.outputDir === 'FALLBACK') {
|
||||
this.options.outputDir = path.join(this.options.cwd, '_site');
|
||||
}
|
||||
if (this.options.inputDir instanceof URL) {
|
||||
this.options.inputDir = this.options.inputDir.pathname;
|
||||
}
|
||||
@@ -122,7 +130,6 @@ export class RocketCli {
|
||||
}
|
||||
|
||||
async prepare() {
|
||||
await this.clearOutputDirs();
|
||||
if (!this.options.presets) {
|
||||
return;
|
||||
}
|
||||
@@ -177,9 +184,9 @@ export class RocketCli {
|
||||
let pluginsMeta = [
|
||||
{ plugin: RocketStart, options: {} },
|
||||
{ plugin: RocketBuild, options: {} },
|
||||
{ plugin: RocketInit, options: {} },
|
||||
// { plugin: RocketLint },
|
||||
{ plugin: RocketLint, options: {} },
|
||||
{ plugin: RocketUpgrade, options: {} },
|
||||
{ plugin: RocketPreview, options: {} },
|
||||
];
|
||||
|
||||
if (Array.isArray(this.options.setupCliPlugins)) {
|
||||
@@ -230,10 +237,13 @@ export class RocketCli {
|
||||
}
|
||||
}
|
||||
|
||||
async clearOutputDirs() {
|
||||
async clearOutputDir() {
|
||||
if (this.options.outputDir && existsSync(this.options.outputDir)) {
|
||||
await rm(this.options.outputDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
async clearOutputDevDir() {
|
||||
if (this.options.outputDevDir && existsSync(this.options.outputDevDir)) {
|
||||
await rm(this.options.outputDevDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
export class RocketInit {
|
||||
/**
|
||||
* @param {import('commander').Command} program
|
||||
* @param {import('./RocketCli.js').RocketCli} cli
|
||||
*/
|
||||
async setupCommand(program, cli) {
|
||||
this.cli = cli;
|
||||
|
||||
program
|
||||
.command('init')
|
||||
.option(
|
||||
'-o, --output-dir <path>',
|
||||
'path to where to put the source files [default to current directory]',
|
||||
)
|
||||
.action(async cliOptions => {
|
||||
// cli.setOptions(cliOptions);
|
||||
this.outputDir = cliOptions.outputDir ? path.resolve(cliOptions.outputDir) : process.cwd();
|
||||
|
||||
await this.init();
|
||||
});
|
||||
}
|
||||
|
||||
async init() {
|
||||
if (!this.outputDir) {
|
||||
return;
|
||||
}
|
||||
|
||||
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
const initFilesDir = path.join(moduleDir, 'init-files');
|
||||
const packageJsonPath = path.join(this.outputDir, 'package.json');
|
||||
|
||||
if (!(await fs.pathExists(packageJsonPath))) {
|
||||
await fs.writeJson(packageJsonPath, {});
|
||||
}
|
||||
|
||||
await fs.copy(initFilesDir, this.outputDir, {
|
||||
errorOnExist: true,
|
||||
filter: file => !(file.endsWith('_gitignore') || file.endsWith('README.md')),
|
||||
});
|
||||
|
||||
const packageJson = await fs.readJson(packageJsonPath);
|
||||
|
||||
await fs.writeJson(
|
||||
packageJsonPath,
|
||||
{
|
||||
...packageJson,
|
||||
type: 'module',
|
||||
scripts: {
|
||||
...packageJson.scripts,
|
||||
start: 'NODE_DEBUG=engine:rendering rocket start --open',
|
||||
build: 'NODE_DEBUG=engine:rendering rocket build',
|
||||
},
|
||||
imports: {
|
||||
'#images/*': './docs/__shared/*',
|
||||
},
|
||||
exports: {
|
||||
'.': './src/index.js',
|
||||
},
|
||||
},
|
||||
{ spaces: 2 },
|
||||
);
|
||||
|
||||
const gitignorePath = path.join(this.outputDir, '.gitignore');
|
||||
await fs.ensureFile(gitignorePath);
|
||||
await fs.appendFile(
|
||||
gitignorePath,
|
||||
await fs.readFile(path.join(initFilesDir, '_gitignore'), 'utf8'),
|
||||
);
|
||||
|
||||
const readmePath = path.join(this.outputDir, 'README.md');
|
||||
await fs.ensureFile(readmePath);
|
||||
await fs.appendFile(
|
||||
readmePath,
|
||||
await fs.readFile(path.join(initFilesDir, '_gitignore'), 'utf8'),
|
||||
);
|
||||
|
||||
console.log('All files have been created 🎉');
|
||||
console.log('Start developing by running `npm start`');
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
@@ -1,81 +1,85 @@
|
||||
// /* eslint-disable */
|
||||
// // @ts-nocheck
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
|
||||
// /** @typedef {import('../types/main').RocketCliOptions} RocketCliOptions */
|
||||
// @ts-ignore
|
||||
import { CheckHtmlLinksCli } from 'check-html-links';
|
||||
import { bold, gray } from 'colorette';
|
||||
import { existsSync } from 'fs';
|
||||
import path from 'path';
|
||||
import { buildHtml } from './build/buildHtml.js';
|
||||
|
||||
// import { CheckHtmlLinksCli } from 'check-html-links';
|
||||
export class RocketLint {
|
||||
options = {
|
||||
buildHtml: false,
|
||||
};
|
||||
|
||||
// export class RocketLint {
|
||||
// static pluginName = 'RocketLint';
|
||||
// commands = ['start', 'build', 'lint'];
|
||||
/**
|
||||
* @param {import('commander').Command} program
|
||||
* @param {import('./RocketCli.js').RocketCli} cli
|
||||
*/
|
||||
async setupCommand(program, cli) {
|
||||
this.cli = cli;
|
||||
this.active = true;
|
||||
|
||||
// /**
|
||||
// * @param {RocketCliOptions} config
|
||||
// */
|
||||
// setupCommand(config) {
|
||||
// if (config.command === 'lint') {
|
||||
// config.watch = false;
|
||||
// }
|
||||
// return config;
|
||||
// }
|
||||
program
|
||||
.command('lint')
|
||||
.option('-i, --input-dir <path>', 'path to where to search for source files')
|
||||
.option('-b, --build-html', 'do a quick html only build and then check links')
|
||||
.action(async options => {
|
||||
const { cliOptions, ...lintOptions } = options;
|
||||
cli.setOptions({
|
||||
...cliOptions,
|
||||
lint: lintOptions,
|
||||
});
|
||||
this.options = { ...this.options, ...cli.options.lint };
|
||||
cli.activePlugin = this;
|
||||
|
||||
// /**
|
||||
// * @param {object} options
|
||||
// * @param {RocketCliOptions} options.config
|
||||
// * @param {any} options.argv
|
||||
// */
|
||||
// async setup({ config, argv, eleventy }) {
|
||||
// this.__argv = argv;
|
||||
// this.config = {
|
||||
// lintInputDir: config.outputDevDir,
|
||||
// lintExecutesEleventyBefore: true,
|
||||
// ...config,
|
||||
// };
|
||||
// this.eleventy = eleventy;
|
||||
// }
|
||||
await this.lint();
|
||||
});
|
||||
}
|
||||
|
||||
// async lintCommand() {
|
||||
// if (this.config.lintExecutesEleventyBefore) {
|
||||
// await this.eleventy.write();
|
||||
// // updated will trigger linting
|
||||
// } else {
|
||||
// await this.__lint();
|
||||
// }
|
||||
// }
|
||||
async lint() {
|
||||
if (!this.cli) {
|
||||
return;
|
||||
}
|
||||
|
||||
// async __lint() {
|
||||
// if (this.config?.pathPrefix) {
|
||||
// console.log('INFO: RocketLint currently does not support being used with a pathPrefix');
|
||||
// return;
|
||||
// }
|
||||
// for typescript as `this.cli.options.outputDir` supports other inputs as well
|
||||
// but the cli will normalize it to a string before calling plugins
|
||||
if (
|
||||
typeof this.cli.options.outputDevDir !== 'string' ||
|
||||
typeof this.cli.options.outputDir !== 'string'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// const checkLinks = new CheckHtmlLinksCli();
|
||||
// checkLinks.setOptions({
|
||||
// ...this.config.checkLinks,
|
||||
// rootDir: this.config.lintInputDir,
|
||||
// printOnError: false,
|
||||
// continueOnError: true,
|
||||
// });
|
||||
if (this.options.buildHtml) {
|
||||
await buildHtml(this.cli);
|
||||
}
|
||||
|
||||
// const { errors, message } = await checkLinks.run();
|
||||
// if (errors.length > 0) {
|
||||
// if (this.config.command === 'start') {
|
||||
// console.log(message);
|
||||
// } else {
|
||||
// throw new Error(message);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
const folderToCheck = this.options.buildHtml
|
||||
? this.cli.options.outputDevDir
|
||||
: this.cli.options.outputDir;
|
||||
|
||||
// async postCommand() {
|
||||
// if (this.config.watch === false) {
|
||||
// await this.__lint();
|
||||
// }
|
||||
// }
|
||||
const rootIndexHtml = path.join(folderToCheck, 'index.html');
|
||||
if (!existsSync(rootIndexHtml)) {
|
||||
console.log(`${bold(`👀 Linting Production Build`)}`);
|
||||
console.log('');
|
||||
console.log(` 🛑 No index.html found in the build directory ${gray(`${rootIndexHtml}`)}`);
|
||||
console.log(' 🤔 Did you forget to run `rocket build` before?');
|
||||
console.log('');
|
||||
return;
|
||||
}
|
||||
|
||||
// async updated() {
|
||||
// if (this.config.watch === true) {
|
||||
// await this.__lint();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { buildHtml: _drop, ...userCheckHtmlLinksOptions } = this.options;
|
||||
|
||||
const checkLinks = new CheckHtmlLinksCli();
|
||||
checkLinks.setOptions({
|
||||
rootDir: folderToCheck,
|
||||
printOnError: true,
|
||||
continueOnError: false,
|
||||
...userCheckHtmlLinksOptions,
|
||||
});
|
||||
|
||||
await checkLinks.run();
|
||||
}
|
||||
}
|
||||
|
||||
79
packages/cli/src/RocketPreview.js
Executable file
79
packages/cli/src/RocketPreview.js
Executable file
@@ -0,0 +1,79 @@
|
||||
import { logPreviewMessage } from './preview/logPreviewMessage.js';
|
||||
import { startDevServer } from '@web/dev-server';
|
||||
import path from 'path';
|
||||
import { existsSync } from 'fs';
|
||||
import { gray, bold } from 'colorette';
|
||||
|
||||
export class RocketPreview {
|
||||
/**
|
||||
* @param {import('commander').Command} program
|
||||
* @param {import('./RocketCli.js').RocketCli} cli
|
||||
*/
|
||||
async setupCommand(program, cli) {
|
||||
this.cli = cli;
|
||||
this.active = true;
|
||||
|
||||
program
|
||||
.command('preview')
|
||||
.option('-i, --input-dir <path>', 'path to the folder with the build html files')
|
||||
.option('-o, --open', 'automatically open the browser')
|
||||
.action(async cliOptions => {
|
||||
cli.setOptions(cliOptions);
|
||||
cli.activePlugin = this;
|
||||
|
||||
await this.preview();
|
||||
});
|
||||
}
|
||||
|
||||
async preview() {
|
||||
if (!this.cli) {
|
||||
return;
|
||||
}
|
||||
|
||||
// for typescript as `this.cli.options.outputDir` supports other inputs as well
|
||||
// but the cli will normalize it to a string before calling plugins
|
||||
if (
|
||||
typeof this.cli.options.inputDir !== 'string' ||
|
||||
typeof this.cli.options.outputDir !== 'string'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rootIndexHtml = path.join(this.cli.options.outputDir, 'index.html');
|
||||
if (!existsSync(rootIndexHtml)) {
|
||||
console.log(`${bold(`👀 Previewing Production Build`)}`);
|
||||
console.log('');
|
||||
console.log(` 🛑 No index.html found in the build directory ${gray(`${rootIndexHtml}`)}`);
|
||||
console.log(' 🤔 Did you forget to run `rocket build` before?');
|
||||
console.log('');
|
||||
return;
|
||||
}
|
||||
|
||||
/** @type {import('@web/dev-server').DevServerConfig} */
|
||||
const config = {
|
||||
open: this.cli.options.open,
|
||||
rootDir: this.cli.options.outputDir,
|
||||
clearTerminalOnReload: false,
|
||||
};
|
||||
|
||||
try {
|
||||
this.devServer = await startDevServer({
|
||||
config,
|
||||
logStartMessage: false,
|
||||
readCliArgs: false,
|
||||
readFileConfig: false,
|
||||
// argv: this.__argv,
|
||||
});
|
||||
logPreviewMessage({ devServerOptions: this.devServer.config }, console);
|
||||
} catch (e) {
|
||||
console.log('🛑 Starting preview server failed');
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async stop() {
|
||||
if (this.devServer) {
|
||||
await this.devServer.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { fromRollup } from '@web/dev-server-rollup';
|
||||
|
||||
import { Engine } from '@rocket/engine/server';
|
||||
import { logStartMessage } from './start/logStartMessage.js';
|
||||
|
||||
export class RocketStart {
|
||||
/** @type {Engine | undefined} */
|
||||
@@ -30,11 +31,12 @@ export class RocketStart {
|
||||
if (!this.cli) {
|
||||
return;
|
||||
}
|
||||
await this.cli.clearOutputDevDir();
|
||||
|
||||
// TODO: enable URL support in the Engine and remove this "workaround"
|
||||
if (
|
||||
typeof this.cli.options.inputDir !== 'string' ||
|
||||
typeof this.cli.options.outputDir !== 'string'
|
||||
typeof this.cli.options.outputDevDir !== 'string'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -49,7 +51,7 @@ export class RocketStart {
|
||||
this.engine = new Engine();
|
||||
this.engine.setOptions({
|
||||
docsDir: this.cli.options.inputDir,
|
||||
outputDir: this.cli.options.outputDir,
|
||||
outputDir: this.cli.options.outputDevDir,
|
||||
setupPlugins: this.cli.options.setupEnginePlugins,
|
||||
open: this.cli.options.open,
|
||||
longFileHeaderWidth: this.cli.options.longFileHeaderWidth,
|
||||
@@ -60,7 +62,14 @@ export class RocketStart {
|
||||
setupDevServerPlugins: [...this.cli.options.setupDevServerPlugins, ...withWrap],
|
||||
});
|
||||
try {
|
||||
console.log('🚀 Engines online');
|
||||
this.engine.events.on('devServerStarted', () => {
|
||||
if (this.engine?.devServer) {
|
||||
logStartMessage(
|
||||
{ devServerOptions: this.engine.devServer?.config, engine: this.engine },
|
||||
console,
|
||||
);
|
||||
}
|
||||
});
|
||||
await this.engine.start();
|
||||
} catch (e) {
|
||||
console.log('Engine start errored');
|
||||
@@ -71,7 +80,6 @@ export class RocketStart {
|
||||
async stop({ hard = true } = {}) {
|
||||
if (this.engine) {
|
||||
await this.engine.stop({ hard });
|
||||
console.log('🚀 Engines offline');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
28
packages/cli/src/build/buildHtml.js
Normal file
28
packages/cli/src/build/buildHtml.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Engine } from '@rocket/engine/server';
|
||||
|
||||
/**
|
||||
* @param {import('../RocketCli.js').RocketCli} cli
|
||||
* @returns
|
||||
*/
|
||||
export async function buildHtml(cli) {
|
||||
// TODO: enable URL support in the Engine and remove this typescript "workaround"
|
||||
if (typeof cli.options.inputDir !== 'string' || typeof cli.options.outputDevDir !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
await cli.clearOutputDevDir();
|
||||
const engine = new Engine();
|
||||
engine.setOptions({
|
||||
docsDir: cli.options.inputDir,
|
||||
outputDir: cli.options.outputDevDir,
|
||||
setupPlugins: cli.options.setupEnginePlugins,
|
||||
longFileHeaderWidth: cli.options.longFileHeaderWidth,
|
||||
longFileHeaderComment: cli.options.longFileHeaderComment,
|
||||
renderMode: 'production',
|
||||
clearOutputDir: cli.options.clearOutputDir,
|
||||
});
|
||||
console.log('Engine building...');
|
||||
await engine.build({ autoStop: cli.options.buildAutoStop });
|
||||
|
||||
return engine;
|
||||
}
|
||||
88
packages/cli/src/build/buildJavaScriptOptimizedOutput.js
Normal file
88
packages/cli/src/build/buildJavaScriptOptimizedOutput.js
Normal file
@@ -0,0 +1,88 @@
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import path from 'path';
|
||||
import { existsSync } from 'fs';
|
||||
import { rollup } from 'rollup';
|
||||
|
||||
// @ts-ignore
|
||||
import { createMpaConfig, createServiceWorkerConfig } from '@rocket/building-rollup';
|
||||
|
||||
// import { rollupPluginHTML } from '@web/rollup-plugin-html';
|
||||
// import { adjustPluginOptions } from 'plugins-manager';
|
||||
|
||||
/**
|
||||
* @param {import('rollup').RollupOptions} config
|
||||
*/
|
||||
async function buildAndWrite(config) {
|
||||
if (!config.output) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bundle = await rollup(config);
|
||||
|
||||
if (Array.isArray(config.output)) {
|
||||
await bundle.write(config.output[0]);
|
||||
await bundle.write(config.output[1]);
|
||||
} else {
|
||||
await bundle.write(config.output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('../RocketCli.js').RocketCli} cli
|
||||
* @param {import('@rocket/engine/server').Engine} engine
|
||||
* @returns
|
||||
*/
|
||||
export async function buildJavaScriptOptimizedOutput(cli, engine) {
|
||||
const config = cli.options;
|
||||
|
||||
// for typescript as `this.cli.options.outputDir` supports other inputs as well
|
||||
// but the cli will normalize it to a string before calling plugins
|
||||
if (typeof config.outputDir !== 'string' || typeof config.outputDevDir !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
await cli.clearOutputDir();
|
||||
|
||||
// TODO: pathPrefix is currently not supported
|
||||
// const defaultSetupPlugins = [];
|
||||
// if (config.pathPrefix) {
|
||||
// defaultSetupPlugins.push(
|
||||
// adjustPluginOptions(rollupPluginHTML, { absolutePathPrefix: config.pathPrefix }),
|
||||
// );
|
||||
// }
|
||||
|
||||
const mpaConfig = createMpaConfig({
|
||||
input: '**/*.html',
|
||||
output: {
|
||||
dir: config.outputDir,
|
||||
},
|
||||
// custom
|
||||
rootDir: path.resolve(config.outputDevDir),
|
||||
absoluteBaseUrl: config.absoluteBaseUrl,
|
||||
setupPlugins: [
|
||||
// ...defaultSetupPlugins,
|
||||
...config.setupDevServerAndBuildPlugins,
|
||||
...config.setupBuildPlugins,
|
||||
],
|
||||
});
|
||||
const finalConfig =
|
||||
typeof config.adjustBuildOptions === 'function'
|
||||
? config.adjustBuildOptions(mpaConfig)
|
||||
: mpaConfig;
|
||||
await buildAndWrite(finalConfig);
|
||||
|
||||
const { serviceWorkerSourcePath } = config;
|
||||
if (existsSync(serviceWorkerSourcePath)) {
|
||||
const serviceWorkerConfig = createServiceWorkerConfig({
|
||||
input: serviceWorkerSourcePath,
|
||||
output: {
|
||||
file: path.join(path.resolve(config.outputDir), config.serviceWorkerName),
|
||||
},
|
||||
});
|
||||
|
||||
await buildAndWrite(serviceWorkerConfig);
|
||||
}
|
||||
|
||||
// copy static files over
|
||||
await engine.copyPublicFilesTo(config.outputDir);
|
||||
}
|
||||
95
packages/cli/src/build/buildOpenGraphImages.js
Normal file
95
packages/cli/src/build/buildOpenGraphImages.js
Normal file
@@ -0,0 +1,95 @@
|
||||
import { gatherFiles } from '@rocket/engine';
|
||||
import { Engine } from '@rocket/engine/server';
|
||||
import { fromRollup } from '@web/dev-server-rollup';
|
||||
|
||||
import { readFile, unlink, writeFile } from 'fs/promises';
|
||||
|
||||
import puppeteer from 'puppeteer';
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
* @param {import('../RocketCli.js').RocketCli} cli
|
||||
* @returns
|
||||
*/
|
||||
export async function buildOpenGraphImages(cli) {
|
||||
const openGraphFiles = await gatherFiles(cli.options.outputDevDir, {
|
||||
fileEndings: ['.opengraph.html'],
|
||||
});
|
||||
if (openGraphFiles.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: enable URL support in the Engine and remove this typescript "workaround"
|
||||
if (typeof cli.options.inputDir !== 'string' || typeof cli.options.outputDevDir !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
const withWrap = cli.options.setupDevServerAndBuildPlugins
|
||||
? cli.options.setupDevServerAndBuildPlugins.map(modFunction => {
|
||||
modFunction.wrapPlugin = fromRollup;
|
||||
return modFunction;
|
||||
})
|
||||
: [];
|
||||
|
||||
const engine = new Engine();
|
||||
engine.setOptions({
|
||||
docsDir: cli.options.inputDir,
|
||||
outputDir: cli.options.outputDevDir,
|
||||
setupPlugins: cli.options.setupEnginePlugins,
|
||||
open: false,
|
||||
clearOutputDir: false,
|
||||
adjustDevServerOptions: cli.options.adjustDevServerOptions,
|
||||
setupDevServerMiddleware: cli.options.setupDevServerMiddleware,
|
||||
setupDevServerPlugins: [...cli.options.setupDevServerPlugins, ...withWrap],
|
||||
});
|
||||
try {
|
||||
await engine.start();
|
||||
if (!engine?.devServer?.config.port) {
|
||||
return;
|
||||
}
|
||||
|
||||
const browser = await puppeteer.launch();
|
||||
const page = await browser.newPage();
|
||||
|
||||
// In 2022 Twitter & Facebook recommend a size of 1200x628 - we capture with 2 dpr for retina displays
|
||||
await page.setViewport({
|
||||
width: 1200,
|
||||
height: 628,
|
||||
deviceScaleFactor: 2,
|
||||
});
|
||||
|
||||
for (const openGraphFile of openGraphFiles) {
|
||||
const relUrl = path.relative(cli.options.outputDevDir, openGraphFile);
|
||||
const imagePath = openGraphFile.replace('.opengraph.html', '.opengraph.png');
|
||||
const htmlPath = openGraphFile.replace('.opengraph.html', '.html');
|
||||
const relImageUrl = path.basename(imagePath);
|
||||
|
||||
let htmlString = await readFile(htmlPath, 'utf8');
|
||||
if (!htmlString.includes('<meta property="og:image"')) {
|
||||
if (htmlString.includes('</head>')) {
|
||||
htmlString = htmlString.replace(
|
||||
'</head>',
|
||||
[
|
||||
' <meta property="og:image:width" content="2400">',
|
||||
' <meta property="og:image:height" content="1256">',
|
||||
` <meta property="og:image" content="./${relImageUrl}">`,
|
||||
' </head>',
|
||||
].join('\n'),
|
||||
);
|
||||
}
|
||||
}
|
||||
const url = `http://localhost:${engine.devServer.config.port}/${relUrl}`;
|
||||
await page.goto(url, { waitUntil: 'networkidle0' });
|
||||
await page.screenshot({ path: imagePath });
|
||||
|
||||
await unlink(openGraphFile);
|
||||
await writeFile(htmlPath, htmlString);
|
||||
}
|
||||
await browser.close();
|
||||
|
||||
await engine.stop();
|
||||
} catch (e) {
|
||||
console.log('Could not start dev server to generate open graph images');
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
34
packages/cli/src/helpers/infoMessages.js
Normal file
34
packages/cli/src/helpers/infoMessages.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import ip from 'ip';
|
||||
import { white, cyan } from 'colorette';
|
||||
|
||||
/** @typedef {import('@web/dev-server').DevServerConfig} DevServerConfig */
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {DevServerConfig} devServerOptions
|
||||
* @param {string} host
|
||||
* @param {string} path
|
||||
* @returns {string}
|
||||
*/
|
||||
export function createAddress(devServerOptions, host, path) {
|
||||
return `http${devServerOptions.http2 ? 's' : ''}://${host}:${devServerOptions.port}${path}`;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {DevServerConfig} devServerOptions
|
||||
* @param {console} logger
|
||||
* @param {string} openPath
|
||||
*/
|
||||
export function logNetworkAddress(devServerOptions, logger, openPath) {
|
||||
try {
|
||||
const address = ip.address();
|
||||
if (typeof address === 'string') {
|
||||
logger.log(
|
||||
`${white(' 🌐 Network:')} ${cyan(createAddress(devServerOptions, address, openPath))}`,
|
||||
);
|
||||
}
|
||||
} catch (_a) {
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"gitdoc.enabled": false,
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"files.exclude": {
|
||||
"**/*-mdjs-generated.js": true,
|
||||
},
|
||||
"search.exclude": {
|
||||
"**/*-mdjs-generated.js": true,
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
# Welcome to Rocket
|
||||
|
||||
Get started with
|
||||
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
Create new pages by calling them `my-page.rocket.md` and by putting them into `docs/`.
|
||||
@@ -1,8 +0,0 @@
|
||||
## npm
|
||||
node_modules
|
||||
npm-debug.log
|
||||
|
||||
## Rocket ignore files
|
||||
*-mdjs-generated.js
|
||||
_site
|
||||
_site-dev
|
||||
@@ -1,6 +0,0 @@
|
||||
import { rocketLaunch } from '@rocket/launch';
|
||||
|
||||
export default /** @type {import('@rocket/cli').RocketCliOptions} */ ({
|
||||
absoluteBaseUrl: 'http://localhost:8080',
|
||||
presets: [rocketLaunch()],
|
||||
});
|
||||
@@ -1,29 +0,0 @@
|
||||
```js server
|
||||
/* START - Rocket auto generated - do not touch */
|
||||
export const sourceRelativeFilePath = '10--guides/10--first-pages/10--getting-started.rocket.md';
|
||||
import { pageTree, layout, html } from '../../recursive.data.js';
|
||||
export { pageTree, layout, html };
|
||||
/* END - Rocket auto generated - do not touch */
|
||||
```
|
||||
|
||||
# Getting Started
|
||||
|
||||
Rocket has the following prerequisites:
|
||||
|
||||
- [Node 14+](https://nodejs.org/en/)
|
||||
|
||||
Make sure they are installed before proceeding.
|
||||
|
||||
## Setup
|
||||
|
||||
The fastest way to get started is by using an existing preset like the launch preset.
|
||||
|
||||
<inline-notification type="warning">
|
||||
|
||||
If you don't want to use the `module` package type, make sure to rename the generated config file to `rocket.config.mjs`.
|
||||
|
||||
</inline-notification>
|
||||
|
||||
```js script
|
||||
import '@rocket/launch/inline-notification/inline-notification.js';
|
||||
```
|
||||
@@ -1,24 +0,0 @@
|
||||
```js server
|
||||
/* START - Rocket auto generated - do not touch */
|
||||
export const sourceRelativeFilePath = '10--guides/10--first-pages/20--custom-layout.rocket.md';
|
||||
import { pageTree } from '../../recursive.data.js';
|
||||
export { pageTree };
|
||||
/* END - Rocket auto generated - do not touch */
|
||||
|
||||
import { html } from 'lit';
|
||||
|
||||
export const layout = data => html`
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<p>A FULLY custom layout</p>
|
||||
${data.content()}
|
||||
<a href="../index.rocket.md">Go back to Guides</a>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
```
|
||||
|
||||
# Custom Layout
|
||||
|
||||
Here is my markdown content.
|
||||
@@ -1,43 +0,0 @@
|
||||
```js server
|
||||
/* START - Rocket auto generated - do not touch */
|
||||
export const sourceRelativeFilePath = '10--guides/10--first-pages/20--images.rocket.md';
|
||||
import { pageTree, layout, html } from '../../recursive.data.js';
|
||||
export { pageTree, layout, html };
|
||||
/* END - Rocket auto generated - do not touch */
|
||||
```
|
||||
|
||||
# Images
|
||||
|
||||
We can add a private package import to your
|
||||
|
||||
👉 `package.json`
|
||||
|
||||
```json
|
||||
"imports": {
|
||||
"#images/*": "./docs/__shared/*"
|
||||
},
|
||||
```
|
||||
|
||||
With that we can then use `resolve:[[npm resolve name]]` in our urls.
|
||||
|
||||
```md
|
||||

|
||||
```
|
||||
|
||||
<div style="width: 50%">
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
You can also include images from dependencies.
|
||||
|
||||
```md
|
||||

|
||||
```
|
||||
|
||||
<div style="width: 50%">
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
@@ -1,12 +0,0 @@
|
||||
/* START - Rocket auto generated - do not touch */
|
||||
export const sourceRelativeFilePath = '10--guides/10--first-pages/index.rocket.js';
|
||||
import { pageTree, layout } from '../../recursive.data.js';
|
||||
export { pageTree, layout };
|
||||
/* END - Rocket auto generated - do not touch */
|
||||
|
||||
import { html } from 'lit';
|
||||
|
||||
export default () => html`
|
||||
<h1>First Pages</h1>
|
||||
<meta name="menu:exclude" content="true" />
|
||||
`;
|
||||
@@ -1,17 +0,0 @@
|
||||
```js server
|
||||
/* START - Rocket auto generated - do not touch */
|
||||
export const sourceRelativeFilePath = '10--guides/index.rocket.md';
|
||||
import { pageTree, layout, html } from '../recursive.data.js';
|
||||
export { pageTree, layout, html };
|
||||
/* END - Rocket auto generated - do not touch */
|
||||
|
||||
import { ChildListMenu } from '@rocket/engine';
|
||||
```
|
||||
|
||||
# Learning Rocket
|
||||
|
||||
<meta name="menu:link.text" content="Guides">
|
||||
|
||||
Rocket helps you generate static pages from Markdown files while giving you the flexibility to sprinkle in some JavaScript where needed.
|
||||
|
||||
${pageTree.renderMenu(new ChildListMenu(), sourceRelativeFilePath)}
|
||||
@@ -1,16 +0,0 @@
|
||||
/* START - Rocket auto generated - do not touch */
|
||||
export const sourceRelativeFilePath = '20--docs/index.rocket.js';
|
||||
import { pageTree, layout, html } from '../recursive.data.js';
|
||||
export { pageTree, layout, html };
|
||||
/* END - Rocket auto generated - do not touch */
|
||||
|
||||
import { ChildListMenu } from '@rocket/engine';
|
||||
|
||||
export default () => html`
|
||||
<meta name="menu:link.text" content="Docs" />
|
||||
|
||||
<h1>Documentation</h1>
|
||||
|
||||
<p>Here you will find all the details for each of the packages/systems we offer.</p>
|
||||
${pageTree.renderMenu(new ChildListMenu(), sourceRelativeFilePath)}
|
||||
`;
|
||||
@@ -1,21 +0,0 @@
|
||||
/* START - Rocket auto generated - do not touch */
|
||||
export const sourceRelativeFilePath = '20--docs/star-wars.rocket.js';
|
||||
import { pageTree, layout, html } from '../recursive.data.js';
|
||||
export { pageTree, layout, html };
|
||||
/* END - Rocket auto generated - do not touch */
|
||||
|
||||
import cache from '@11ty/eleventy-cache-assets';
|
||||
|
||||
const films = await cache('https://swapi.dev/api/films/', {
|
||||
duration: '1d',
|
||||
type: 'json',
|
||||
});
|
||||
|
||||
export default () => html`
|
||||
<h1>Star Wars</h1>
|
||||
|
||||
<h2>Films:</h2>
|
||||
<ul>
|
||||
${films.results.map(film => html`<li>${film.title} (${film.release_date})</li>`)}
|
||||
</ul>
|
||||
`;
|
||||
@@ -1,11 +0,0 @@
|
||||
/* START - Rocket auto generated - do not touch */
|
||||
export const sourceRelativeFilePath = '404.html.rocket.js';
|
||||
import { pageTree, html } from './recursive.data.js';
|
||||
export { pageTree, html };
|
||||
/* END - Rocket auto generated - do not touch */
|
||||
|
||||
import { Layout404 } from '@rocket/launch';
|
||||
|
||||
export const layout = new Layout404();
|
||||
|
||||
export default () => '';
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 12 KiB |
@@ -1,45 +0,0 @@
|
||||
export const footerMenu = [
|
||||
{
|
||||
name: 'Discover',
|
||||
children: [
|
||||
{
|
||||
text: 'Blog',
|
||||
href: '/blog/',
|
||||
},
|
||||
{
|
||||
text: 'Help and Feedback',
|
||||
href: 'https://github.com/modernweb-dev/rocket/issues',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Follow',
|
||||
children: [
|
||||
{
|
||||
text: 'GitHub',
|
||||
href: 'https://github.com/modernweb-dev/rocket',
|
||||
},
|
||||
{
|
||||
text: 'Twitter',
|
||||
href: 'https://twitter.com/modern_web_dev',
|
||||
},
|
||||
{
|
||||
text: 'Slack',
|
||||
href: '/about/slack/',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Support',
|
||||
children: [
|
||||
{
|
||||
text: 'Sponsor',
|
||||
href: '/about/sponsor/',
|
||||
},
|
||||
{
|
||||
text: 'Contribute',
|
||||
href: 'https://github.com/modernweb-dev/rocket/blob/main/CONTRIBUTING.md',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 178 KiB |
@@ -1,52 +0,0 @@
|
||||
/* START - Rocket auto generated - do not touch */
|
||||
export const sourceRelativeFilePath = 'index.rocket.js';
|
||||
import { pageTree } from './recursive.data.js';
|
||||
export { pageTree };
|
||||
/* END - Rocket auto generated - do not touch */
|
||||
|
||||
import { html } from 'lit';
|
||||
|
||||
import { LayoutHome } from '@rocket/launch';
|
||||
import { footerMenu } from './__shared/footerMenu.js';
|
||||
|
||||
export const layout = new LayoutHome({
|
||||
pageTree,
|
||||
footerMenu,
|
||||
slogan: 'The modern web setup for static sites with a sprinkle of JavaScript.',
|
||||
callToActionItems: [
|
||||
{ text: 'Follow Guides', href: '/guides/' },
|
||||
{ text: 'Browse Docs', href: '/docs/' },
|
||||
],
|
||||
background: '/home-background.svg',
|
||||
reasonHeader: 'Why Rocket?',
|
||||
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.',
|
||||
},
|
||||
{
|
||||
header: 'Zero Configuration',
|
||||
text: 'Automatic code splitting, filesystem based routing, and JavaScript in Markdown.',
|
||||
},
|
||||
{
|
||||
header: 'Meta Framework',
|
||||
text: html`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',
|
||||
text: 'Optimized for a smaller build size, faster dev compilation and dozens of other improvements.',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
export default () => '';
|
||||
@@ -1,121 +0,0 @@
|
||||
{
|
||||
"title": "Rocket | Rocket",
|
||||
"h1": "Rocket",
|
||||
"name": "Rocket",
|
||||
"menuLinkText": "Rocket",
|
||||
"url": "/",
|
||||
"outputRelativeFilePath": "index.html",
|
||||
"sourceRelativeFilePath": "index.rocket.js",
|
||||
"level": 0,
|
||||
"children": [
|
||||
{
|
||||
"title": "Learning Rocket | Rocket",
|
||||
"h1": "Learning Rocket",
|
||||
"headlinesWithId": [
|
||||
{
|
||||
"text": "Learning Rocket",
|
||||
"id": "learning-rocket",
|
||||
"level": 1
|
||||
}
|
||||
],
|
||||
"menuLinkText": "Guides",
|
||||
"name": "Learning Rocket",
|
||||
"url": "/guides/",
|
||||
"outputRelativeFilePath": "guides/index.html",
|
||||
"sourceRelativeFilePath": "10--guides/index.rocket.md",
|
||||
"level": 1,
|
||||
"children": [
|
||||
{
|
||||
"title": "First Pages | Rocket",
|
||||
"h1": "First Pages",
|
||||
"menuNoLink": true,
|
||||
"name": "First Pages",
|
||||
"menuLinkText": "First Pages",
|
||||
"url": "/guides/first-pages/",
|
||||
"outputRelativeFilePath": "guides/first-pages/index.html",
|
||||
"sourceRelativeFilePath": "10--guides/10--first-pages/index.rocket.js",
|
||||
"level": 2,
|
||||
"children": [
|
||||
{
|
||||
"title": "Getting Started | Rocket",
|
||||
"h1": "Getting Started",
|
||||
"headlinesWithId": [
|
||||
{
|
||||
"text": "Getting Started",
|
||||
"id": "getting-started",
|
||||
"level": 1
|
||||
},
|
||||
{
|
||||
"text": "Setup",
|
||||
"id": "setup",
|
||||
"level": 2
|
||||
}
|
||||
],
|
||||
"name": "Getting Started",
|
||||
"menuLinkText": "Getting Started",
|
||||
"url": "/guides/first-pages/getting-started/",
|
||||
"outputRelativeFilePath": "guides/first-pages/getting-started/index.html",
|
||||
"sourceRelativeFilePath": "10--guides/10--first-pages/10--getting-started.rocket.md",
|
||||
"level": 3
|
||||
},
|
||||
{
|
||||
"h1": "Custom Layout",
|
||||
"headlinesWithId": [
|
||||
{
|
||||
"text": "Custom Layout",
|
||||
"id": "custom-layout",
|
||||
"level": 1
|
||||
}
|
||||
],
|
||||
"name": "Custom Layout",
|
||||
"menuLinkText": "Custom Layout",
|
||||
"url": "/guides/first-pages/custom-layout/",
|
||||
"outputRelativeFilePath": "guides/first-pages/custom-layout/index.html",
|
||||
"sourceRelativeFilePath": "10--guides/10--first-pages/20--custom-layout.rocket.md",
|
||||
"level": 3
|
||||
},
|
||||
{
|
||||
"title": "Images | Rocket",
|
||||
"h1": "Images",
|
||||
"headlinesWithId": [
|
||||
{
|
||||
"text": "Images",
|
||||
"id": "images",
|
||||
"level": 1
|
||||
}
|
||||
],
|
||||
"name": "Images",
|
||||
"menuLinkText": "Images",
|
||||
"url": "/guides/first-pages/images/",
|
||||
"outputRelativeFilePath": "guides/first-pages/images/index.html",
|
||||
"sourceRelativeFilePath": "10--guides/10--first-pages/20--images.rocket.md",
|
||||
"level": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Documentation | Rocket",
|
||||
"menuLinkText": "Docs",
|
||||
"h1": "Documentation",
|
||||
"name": "Documentation",
|
||||
"url": "/docs/",
|
||||
"outputRelativeFilePath": "docs/index.html",
|
||||
"sourceRelativeFilePath": "20--docs/index.rocket.js",
|
||||
"level": 1,
|
||||
"children": [
|
||||
{
|
||||
"title": "Star Wars | Rocket",
|
||||
"h1": "Star Wars",
|
||||
"name": "Star Wars",
|
||||
"menuLinkText": "Star Wars",
|
||||
"url": "/docs/star-wars/",
|
||||
"outputRelativeFilePath": "docs/star-wars/index.html",
|
||||
"sourceRelativeFilePath": "20--docs/star-wars.rocket.js",
|
||||
"level": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { PageTree } from '@rocket/engine';
|
||||
import { LayoutSidebar } from '@rocket/launch';
|
||||
import { footerMenu } from './__shared/footerMenu.js';
|
||||
import { html } from 'lit';
|
||||
|
||||
export const pageTree = new PageTree({
|
||||
inputDir: new URL('./', import.meta.url),
|
||||
outputDir: new URL('../_site', import.meta.url),
|
||||
});
|
||||
await pageTree.restore();
|
||||
|
||||
export const layout = new LayoutSidebar({ pageTree, footerMenu });
|
||||
|
||||
export { html };
|
||||
|
||||
// export const openGraphLayout = new OpenGraphLayoutLogo();
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "esnext",
|
||||
"outDir": "./dist-types",
|
||||
"rootDir": ".",
|
||||
"composite": true,
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"target": "esnext",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"downlevelIteration": true,
|
||||
"strict": true,
|
||||
"moduleResolution": "Node",
|
||||
"typeRoots": ["@types", "./types"],
|
||||
"types": ["node"],
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"lib": [
|
||||
"DOM",
|
||||
"DOM.Iterable",
|
||||
"ES6",
|
||||
"ES2017",
|
||||
// Allows array.flatMap. import `array-flat-polyfill` to cover node10
|
||||
"ES2019.array",
|
||||
"ScriptHost"
|
||||
]
|
||||
}
|
||||
}
|
||||
33
packages/cli/src/preview/logPreviewMessage.js
Normal file
33
packages/cli/src/preview/logPreviewMessage.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import { white, bold, cyan, gray } from 'colorette';
|
||||
import { createAddress, logNetworkAddress } from '../helpers/infoMessages.js';
|
||||
|
||||
/** @typedef {import('@web/dev-server').DevServerConfig} DevServerConfig */
|
||||
|
||||
/**
|
||||
* @param {{ devServerOptions: DevServerConfig}} options
|
||||
* @param {console} logger
|
||||
*/
|
||||
export function logPreviewMessage({ devServerOptions }, logger) {
|
||||
const prettyHost = devServerOptions.hostname ?? 'localhost';
|
||||
let openPath = typeof devServerOptions.open === 'string' ? devServerOptions.open : '/';
|
||||
if (!openPath.startsWith('/')) {
|
||||
openPath = `/${openPath}`;
|
||||
}
|
||||
|
||||
logger.log(`${bold(`👀 Previewing Production Build`)}`);
|
||||
logger.log('');
|
||||
logger.log(
|
||||
`${white(' 🚧 Local:')} ${cyan(createAddress(devServerOptions, prettyHost, openPath))}`,
|
||||
);
|
||||
logNetworkAddress(devServerOptions, logger, openPath);
|
||||
const sourceDir = devServerOptions.rootDir;
|
||||
if (sourceDir) {
|
||||
logger.log(`${white(' 📝 Source:')} ${cyan(sourceDir)}`);
|
||||
}
|
||||
logger.log('');
|
||||
logger.log(
|
||||
gray(
|
||||
'If what you see works as expected then you can upload "source" to your production web server.',
|
||||
),
|
||||
);
|
||||
}
|
||||
24
packages/cli/src/start/logStartMessage.js
Normal file
24
packages/cli/src/start/logStartMessage.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import { white, bold, cyan, gray } from 'colorette';
|
||||
import { createAddress, logNetworkAddress } from '../helpers/infoMessages.js';
|
||||
|
||||
/** @typedef {import('@web/dev-server').DevServerConfig} DevServerConfig */
|
||||
|
||||
/**
|
||||
* @param {{ devServerOptions: DevServerConfig, engine: import('@rocket/engine/server').Engine}} options
|
||||
* @param {console} logger
|
||||
*/
|
||||
export function logStartMessage({ devServerOptions, engine }, logger) {
|
||||
const prettyHost = devServerOptions.hostname ?? 'localhost';
|
||||
let openPath = typeof devServerOptions.open === 'string' ? devServerOptions.open : '/';
|
||||
if (!openPath.startsWith('/')) {
|
||||
openPath = `/${openPath}`;
|
||||
}
|
||||
|
||||
logger.log(`${bold(`🚀 Rocket Engine`)} ${gray(`v${engine.getVersion()}`)}`);
|
||||
logger.log('');
|
||||
logger.log(
|
||||
`${white(' 🚧 Local:')} ${cyan(createAddress(devServerOptions, prettyHost, openPath))}`,
|
||||
);
|
||||
logNetworkAddress(devServerOptions, logger, openPath);
|
||||
logger.log('');
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { setupTestCli } from './test-helpers.js';
|
||||
|
||||
export function prepareTestCli(importMetaUrl) {
|
||||
const dir = path.dirname(fileURLToPath(importMetaUrl));
|
||||
return (cwd, cliOptions = ['build'], options = {}) => setupTestCli(cwd, cliOptions, options, dir);
|
||||
return fullOptions => setupTestCli({ dir, ...fullOptions });
|
||||
}
|
||||
|
||||
const { expect } = chai;
|
||||
|
||||
@@ -60,7 +60,13 @@ function cleanupLitMarkersFn(text) {
|
||||
return newText;
|
||||
}
|
||||
|
||||
export async function setupTestCli(cwd, cliOptions = ['build'], options = {}, dir) {
|
||||
export async function setupTestCli({
|
||||
cwd,
|
||||
cliOptions = ['build'],
|
||||
options = {},
|
||||
testOptions = {},
|
||||
dir,
|
||||
}) {
|
||||
const resolvedCwd = path.join(dir, cwd.split('/').join(path.sep));
|
||||
const useOptions = { buildOptimize: false, buildAutoStop: false, ...options, cwd: resolvedCwd };
|
||||
if (useOptions.inputDir) {
|
||||
@@ -69,6 +75,18 @@ export async function setupTestCli(cwd, cliOptions = ['build'], options = {}, di
|
||||
useOptions.outputDir = path.join(resolvedCwd, '__output');
|
||||
useOptions.outputDevDir = path.join(resolvedCwd, '__output-dev');
|
||||
|
||||
const capturedLogs = [];
|
||||
const origLog = console.log;
|
||||
const origError = console.error;
|
||||
if (testOptions.captureLogs) {
|
||||
console.log = msg => {
|
||||
capturedLogs.push(msg);
|
||||
};
|
||||
console.error = msg => {
|
||||
capturedLogs.push(msg);
|
||||
};
|
||||
}
|
||||
|
||||
const cli = new RocketCli({
|
||||
argv: [process.argv[0], new URL('../src/cli.js', import.meta.url).pathname, ...cliOptions],
|
||||
});
|
||||
@@ -184,6 +202,10 @@ export async function setupTestCli(cwd, cliOptions = ['build'], options = {}, di
|
||||
|
||||
async function cleanup() {
|
||||
await cli.stop({ hard: false });
|
||||
if (testOptions.captureLogs) {
|
||||
console.log = origLog;
|
||||
console.error = origError;
|
||||
}
|
||||
}
|
||||
|
||||
async function build() {
|
||||
@@ -243,5 +265,6 @@ export async function setupTestCli(cwd, cliOptions = ['build'], options = {}, di
|
||||
renameSource,
|
||||
backupOrRestoreSource,
|
||||
restoreSource,
|
||||
capturedLogs,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ const { expect } = chai;
|
||||
|
||||
describe('Config', () => {
|
||||
it('01: no config file', async () => {
|
||||
const { build, readOutput, readDevOutput } = await setupTestCli(
|
||||
'fixtures/01-config/01-no-config/',
|
||||
undefined,
|
||||
{ buildOptimize: true },
|
||||
);
|
||||
const { build, readOutput, readDevOutput } = await setupTestCli({
|
||||
cwd: 'fixtures/01-config/01-no-config/',
|
||||
options: { buildOptimize: true },
|
||||
testOptions: { captureLogs: true },
|
||||
});
|
||||
await build();
|
||||
|
||||
expect(readOutput('index.html')).to.equal(
|
||||
@@ -31,14 +31,21 @@ describe('Config', () => {
|
||||
});
|
||||
|
||||
it('02: change input dir', async () => {
|
||||
const { build, readDevOutput } = await setupTestCli('fixtures/01-config/02-change-input-dir/');
|
||||
const { build, readDevOutput } = await setupTestCli({
|
||||
cwd: 'fixtures/01-config/02-change-input-dir/',
|
||||
testOptions: { captureLogs: true },
|
||||
});
|
||||
await build();
|
||||
|
||||
expect(readDevOutput('index.html')).to.equal(['Hello World!'].join('\n'));
|
||||
});
|
||||
|
||||
it('03: can add a middleware (api proxy) to the dev server', async () => {
|
||||
const { cleanup, cli } = await setupTestCli('fixtures/01-config/03-add-middleware/', ['start']);
|
||||
const { cleanup, cli } = await setupTestCli({
|
||||
cwd: 'fixtures/01-config/03-add-middleware/',
|
||||
cliOptions: ['start'],
|
||||
testOptions: { captureLogs: true },
|
||||
});
|
||||
const apiServer = http.createServer((request, response) => {
|
||||
if (request.url === '/api/message') {
|
||||
response.writeHead(200);
|
||||
@@ -61,20 +68,22 @@ describe('Config', () => {
|
||||
});
|
||||
|
||||
it('04: can add a rollup plugin via setupDevServerAndBuildPlugins to build', async () => {
|
||||
const { build, readOutput } = await setupTestCli(
|
||||
'fixtures/01-config/04-add-rollup-plugin/',
|
||||
undefined,
|
||||
{ buildOptimize: true },
|
||||
);
|
||||
const { build, readOutput } = await setupTestCli({
|
||||
cwd: 'fixtures/01-config/04-add-rollup-plugin/',
|
||||
options: { buildOptimize: true },
|
||||
testOptions: { captureLogs: true },
|
||||
});
|
||||
await build();
|
||||
const inlineModule = await readOutput('e97af63d.js', { format: false });
|
||||
expect(inlineModule).to.equal('var a={test:"data"};console.log(a);\n');
|
||||
});
|
||||
|
||||
it('04a: can add a rollup plugin via setupDevServerAndBuildPlugins to start', async () => {
|
||||
const { cli, cleanup } = await setupTestCli('fixtures/01-config/04-add-rollup-plugin/', [
|
||||
'start',
|
||||
]);
|
||||
const { cli, cleanup } = await setupTestCli({
|
||||
cwd: 'fixtures/01-config/04-add-rollup-plugin/',
|
||||
cliOptions: ['start'],
|
||||
testOptions: { captureLogs: true },
|
||||
});
|
||||
await cli.start();
|
||||
const { port } = cli?.activePlugin?.engine.devServer.config;
|
||||
|
||||
@@ -88,9 +97,10 @@ describe('Config', () => {
|
||||
});
|
||||
|
||||
it('05: long file header comments', async () => {
|
||||
const { build, readSource } = await setupTestCli(
|
||||
'fixtures/01-config/05-long-file-header-comment/',
|
||||
);
|
||||
const { build, readSource } = await setupTestCli({
|
||||
cwd: 'fixtures/01-config/05-long-file-header-comment/',
|
||||
testOptions: { captureLogs: true },
|
||||
});
|
||||
await build();
|
||||
|
||||
expect(readSource('index.rocket.js', { format: false })).to.equal(
|
||||
|
||||
@@ -5,13 +5,13 @@ const { expect } = chai;
|
||||
|
||||
describe('Build', () => {
|
||||
it('01: copy public files', async () => {
|
||||
const { build, readOutput, outputExists, readDevOutput } = await setupTestCli(
|
||||
'fixtures/02-build/01-copy-public-files/',
|
||||
undefined,
|
||||
{
|
||||
const { build, readOutput, outputExists, readDevOutput } = await setupTestCli({
|
||||
cwd: 'fixtures/02-build/01-copy-public-files/',
|
||||
options: {
|
||||
buildOptimize: true,
|
||||
},
|
||||
);
|
||||
testOptions: { captureLogs: true },
|
||||
});
|
||||
await build();
|
||||
|
||||
expect(readOutput('index.html')).to.equal(
|
||||
|
||||
@@ -6,7 +6,11 @@ const { expect } = chai;
|
||||
describe('Upgrade System', () => {
|
||||
it('2021-09-menu', async () => {
|
||||
const { build, sourceExists, readSource, backupOrRestoreSource, restoreSource } =
|
||||
await setupTestCli('fixtures/03-upgrade/2022-03-menu', ['upgrade']);
|
||||
await setupTestCli({
|
||||
cwd: 'fixtures/03-upgrade/2022-03-menu',
|
||||
cliOptions: ['upgrade'],
|
||||
testOptions: { captureLogs: true },
|
||||
});
|
||||
await backupOrRestoreSource();
|
||||
|
||||
await build();
|
||||
|
||||
@@ -5,13 +5,13 @@ const { expect } = chai;
|
||||
|
||||
describe('Open Graph', () => {
|
||||
it('generates the image and adds the meta tags', async () => {
|
||||
const { build, readOutput, outputExists } = await setupTestCli(
|
||||
'fixtures/04-open-graph/01-generate-image-and-inject-meta',
|
||||
undefined,
|
||||
{
|
||||
const { build, readOutput, outputExists } = await setupTestCli({
|
||||
cwd: 'fixtures/04-open-graph/01-generate-image-and-inject-meta',
|
||||
options: {
|
||||
buildOptimize: true,
|
||||
},
|
||||
);
|
||||
testOptions: { captureLogs: true },
|
||||
});
|
||||
await build();
|
||||
|
||||
expect(readOutput('index.html', { replaceImageHashes: true })).to.equal(
|
||||
@@ -35,13 +35,13 @@ describe('Open Graph', () => {
|
||||
});
|
||||
|
||||
it('handles multiple pages', async () => {
|
||||
const { build, readOutput } = await setupTestCli(
|
||||
'fixtures/04-open-graph/02-multiple-pages',
|
||||
undefined,
|
||||
{
|
||||
const { build, readOutput } = await setupTestCli({
|
||||
cwd: 'fixtures/04-open-graph/02-multiple-pages',
|
||||
options: {
|
||||
buildOptimize: true,
|
||||
},
|
||||
);
|
||||
testOptions: { captureLogs: true },
|
||||
});
|
||||
await build();
|
||||
|
||||
expect(readOutput('index.html', { replaceImageHashes: true })).to.equal(
|
||||
|
||||
25
packages/cli/test-node/05-start.test.js
Normal file
25
packages/cli/test-node/05-start.test.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import chai from 'chai';
|
||||
import { white, bold } from 'colorette';
|
||||
|
||||
import { setupTestCli } from './test-helpers.js';
|
||||
|
||||
const { expect } = chai;
|
||||
|
||||
describe('Start', () => {
|
||||
it('Start Message', async () => {
|
||||
const { cli, capturedLogs, cleanup } = await setupTestCli({
|
||||
cwd: 'fixtures/05-start/01-start-message',
|
||||
cliOptions: ['start'],
|
||||
testOptions: { captureLogs: true },
|
||||
});
|
||||
|
||||
await cli.start();
|
||||
await cleanup();
|
||||
|
||||
expect(capturedLogs[0].startsWith(`${bold(`🚀 Rocket Engine`)} `)).to.be.true;
|
||||
expect(capturedLogs[1]).to.equal('');
|
||||
expect(capturedLogs[2].startsWith(`${white(' 🚧 Local:')}`)).to.be.true;
|
||||
expect(capturedLogs[3].startsWith(`${white(' 🌐 Network:')}`)).to.be.true;
|
||||
expect(capturedLogs[4]).to.equal('');
|
||||
});
|
||||
});
|
||||
49
packages/cli/test-node/06-preview.test.js
Normal file
49
packages/cli/test-node/06-preview.test.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import chai from 'chai';
|
||||
import { white, bold, gray } from 'colorette';
|
||||
|
||||
import { setupTestCli } from './test-helpers.js';
|
||||
|
||||
const { expect } = chai;
|
||||
|
||||
describe('Preview', () => {
|
||||
it('01: Preview Message', async () => {
|
||||
const { cli, capturedLogs, cleanup } = await setupTestCli({
|
||||
cwd: 'fixtures/06-preview/01-preview-message',
|
||||
cliOptions: ['preview'],
|
||||
testOptions: { captureLogs: true },
|
||||
});
|
||||
|
||||
await cli.start();
|
||||
await cleanup();
|
||||
|
||||
expect(capturedLogs[0]).to.equal(`${bold(`👀 Previewing Production Build`)}`);
|
||||
expect(capturedLogs[1]).to.equal('');
|
||||
expect(capturedLogs[2].startsWith(`${white(' 🚧 Local:')}`)).to.be.true;
|
||||
expect(capturedLogs[3].startsWith(`${white(' 🌐 Network:')}`)).to.be.true;
|
||||
expect(capturedLogs[4].startsWith(`${white(' 📝 Source:')}`)).to.be.true;
|
||||
expect(capturedLogs[5]).to.equal('');
|
||||
expect(capturedLogs[6]).to.equal(
|
||||
`${gray(
|
||||
'If what you see works as expected then you can upload "source" to your production web server.',
|
||||
)}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('02: Error Message if there is no build output', async () => {
|
||||
const { cli, capturedLogs, cleanup } = await setupTestCli({
|
||||
cwd: 'fixtures/06-preview/02-error-no-build',
|
||||
cliOptions: ['preview'],
|
||||
testOptions: { captureLogs: true },
|
||||
});
|
||||
|
||||
await cli.start();
|
||||
await cleanup();
|
||||
|
||||
expect(capturedLogs[0]).to.equal(`${bold(`👀 Previewing Production Build`)}`);
|
||||
expect(capturedLogs[1]).to.equal('');
|
||||
expect(capturedLogs[2].startsWith(` 🛑 No index.html found in the build directory`)).to.be
|
||||
.true;
|
||||
expect(capturedLogs[3]).to.equal(' 🤔 Did you forget to run `rocket build` before?');
|
||||
expect(capturedLogs[4]).to.equal('');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
/* START - Rocket auto generated - do not touch */
|
||||
export const sourceRelativeFilePath = 'index.rocket.js';
|
||||
/* END - Rocket auto generated - do not touch */
|
||||
|
||||
export default () => 'Hello World!';
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "index.rocket.js",
|
||||
"menuLinkText": "index.rocket.js",
|
||||
"url": "/",
|
||||
"outputRelativeFilePath": "index.html",
|
||||
"sourceRelativeFilePath": "index.rocket.js",
|
||||
"level": 0
|
||||
}
|
||||
1
packages/cli/test-node/fixtures/06-preview/01-preview-message/.gitignore
vendored
Normal file
1
packages/cli/test-node/fixtures/06-preview/01-preview-message/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!__output
|
||||
@@ -0,0 +1 @@
|
||||
so preview does not stop
|
||||
5
packages/cli/types/main.d.ts
vendored
5
packages/cli/types/main.d.ts
vendored
@@ -47,6 +47,11 @@ export interface FullRocketCliOptions extends Pick<FullRocketPreset, PresetKeys>
|
||||
// rarely used
|
||||
configFile: string;
|
||||
outputDevDir: URL | string;
|
||||
|
||||
lint: {
|
||||
buildHtml: boolean;
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
|
||||
export type RocketCliOptions = Partial<FullRocketCliOptions>;
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"dependencies": {
|
||||
"@webcomponents/template-shadowroot": "^0.1.0",
|
||||
"fontawesome-free": "^1.0.4",
|
||||
"lit": "^2.2.5"
|
||||
"lit": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"types": "./dist-types/exports/index.d.ts",
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
# @rocket/drawer
|
||||
|
||||
## 0.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 1f14105: Add export map which enables side effect import via `@rocket/drawer/define`
|
||||
|
||||
## 0.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 445b028: Update to latest lit, @open-wc, @lion packages
|
||||
|
||||
## 0.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 0b64116: Update @lion dependencies
|
||||
|
||||
## 0.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 897892d: bump dependencies
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- d955b43: reset translation on teardown overlay controller
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 1971f5d: Initial Release
|
||||
@@ -1,7 +0,0 @@
|
||||
# Rocket Drawer
|
||||
|
||||
For mobile navigation on [Rocket sites](https://rocket.modern-web.dev/).
|
||||
|
||||
--
|
||||
|
||||
Inspired by [kenchris's menu-drawer](https://github.com/kenchris/websensor-compass/blob/master/scripts/menu-drawer.js).
|
||||
@@ -1 +0,0 @@
|
||||
export { RocketDrawer } from './src/RocketDrawer.js';
|
||||
@@ -1,45 +0,0 @@
|
||||
{
|
||||
"name": "@rocket/drawer",
|
||||
"version": "0.1.5",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"description": "Rocket stuff",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/modernweb-dev/rocket.git",
|
||||
"directory": "packages/drawer"
|
||||
},
|
||||
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
|
||||
"main": "index.js",
|
||||
"exports": {
|
||||
".": "./index.js",
|
||||
"./rocket-drawer.js": "./rocket-drawer.js",
|
||||
"./define": "./rocket-drawer.js"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "web-dev-server --node-resolve --root-dir ../../ --open packages/drawer/ --watch",
|
||||
"rocket:build": "node src/build/cli.js -c demo/docs",
|
||||
"rocket:start": "node src/start/cli.js -c demo/docs --root-dir ../../ --open packages/cli/demo/docs/README.md",
|
||||
"start": "npm run rocket:start",
|
||||
"test": "mocha test-node/**/*.test.js test-node/*.test.js",
|
||||
"test:watch": "mocha test-node/**/*.test.js test-node/*.test.js --watch"
|
||||
},
|
||||
"files": [
|
||||
"*.js",
|
||||
"dist-types",
|
||||
"src"
|
||||
],
|
||||
"keywords": [
|
||||
"storybook",
|
||||
"demo",
|
||||
"demo-states",
|
||||
"testing"
|
||||
],
|
||||
"dependencies": {
|
||||
"@lion/overlays": "^0.32.0",
|
||||
"lit": "^2.0.0"
|
||||
},
|
||||
"types": "dist-types/index.d.ts"
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import { RocketDrawer } from './src/RocketDrawer.js';
|
||||
|
||||
customElements.define('rocket-drawer', RocketDrawer);
|
||||
@@ -1,264 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import { LitElement, html } from 'lit-element';
|
||||
import { OverlayMixin, withModalDialogConfig } from '@lion/overlays';
|
||||
|
||||
/** @typedef {import('@lion/overlays/types/OverlayConfig').OverlayConfig} OverlayConfig */
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} el
|
||||
*/
|
||||
function transitionend(el) {
|
||||
return new Promise(resolve => {
|
||||
el.addEventListener('transitionend', resolve, { once: true });
|
||||
});
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
export class RocketDrawer extends OverlayMixin(LitElement) {
|
||||
static get properties() {
|
||||
return {
|
||||
useOverlay: { type: Boolean, reflect: true },
|
||||
useOverlayMediaQuery: { type: String },
|
||||
mediaMatcher: { type: Object },
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
// @ts-ignore
|
||||
_defineOverlayConfig() {
|
||||
return /** @type {OverlayConfig} */ {
|
||||
...withModalDialogConfig(),
|
||||
hidesOnOutsideClick: true,
|
||||
viewportConfig: {
|
||||
placement: 'slide',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
_setupOverlayCtrl() {
|
||||
if (this.useOverlay) {
|
||||
super._setupOverlayCtrl();
|
||||
|
||||
/* eslint-disable no-param-reassign */
|
||||
this._overlayCtrl.transitionHide = async ({ contentNode }) => {
|
||||
contentNode.style.transition = 'transform 0.20s cubic-bezier(0.4, 0.0, 0.2, 1)';
|
||||
contentNode.style.transform = 'translateX(-100%)';
|
||||
await transitionend(contentNode);
|
||||
// contentNode.style.display = 'none';
|
||||
};
|
||||
this._overlayCtrl.transitionShow = async ({ contentNode }) => {
|
||||
contentNode.style.display = 'block';
|
||||
contentNode.style.transform = 'translateX(-100%)';
|
||||
contentNode.style.transition = 'transform 0.25s cubic-bezier(0.4, 0.0, 0.2, 1)';
|
||||
// wait for display block to be "updated in the dom" and then translate otherwise there will be no animation
|
||||
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
contentNode.style.transform = 'translateX(0)';
|
||||
await transitionend(contentNode);
|
||||
};
|
||||
/* eslint-enable no-param-reassign */
|
||||
|
||||
this._overlayCtrl.contentNode.style.transform = 'translateX(-100%)';
|
||||
this._overlayCtrl.contentNode.style.willChange = 'transform';
|
||||
|
||||
// gesture
|
||||
this.containerEl = this._overlayCtrl.contentNode;
|
||||
}
|
||||
}
|
||||
|
||||
_teardownOverlayCtrl() {
|
||||
super._teardownOverlayCtrl();
|
||||
this._overlayCtrl.contentNode.style.transform = 'translateX(0)';
|
||||
}
|
||||
|
||||
/** @param {import('lit-element').PropertyValues } changedProperties */
|
||||
updated(changedProperties) {
|
||||
super.updated(changedProperties);
|
||||
if (changedProperties.has('opened')) {
|
||||
if (this.opened) {
|
||||
document.body.addEventListener('touchstart', this.onGestureStart, { passive: true });
|
||||
} else {
|
||||
document.body.removeEventListener('touchstart', this.onGestureStart);
|
||||
}
|
||||
}
|
||||
|
||||
if (changedProperties.has('useOverlay')) {
|
||||
if (this.useOverlay) {
|
||||
this._setupOverlayCtrl();
|
||||
} else {
|
||||
if (this._overlayCtrl) {
|
||||
this._teardownOverlayCtrl();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changedProperties.has('useOverlayMediaQuery')) {
|
||||
this.mediaMatcher.removeEventListener('change', this.onMatchMedia);
|
||||
|
||||
this.mediaMatcher = window.matchMedia(this.useOverlayMediaQuery);
|
||||
this.mediaMatcher.addEventListener('change', this.onMatchMedia);
|
||||
this.useOverlay = !!this.mediaMatcher.matches;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param { MediaQueryListEvent } query
|
||||
*/
|
||||
onMatchMedia(query) {
|
||||
this.useOverlay = !!query.matches;
|
||||
}
|
||||
|
||||
_setupOpenCloseListeners() {
|
||||
super._setupOpenCloseListeners();
|
||||
if (this._overlayInvokerNode) {
|
||||
this._overlayInvokerNode.addEventListener('click', this.__toggle);
|
||||
}
|
||||
}
|
||||
|
||||
_teardownOpenCloseListeners() {
|
||||
super._teardownOpenCloseListeners();
|
||||
if (this._overlayInvokerNode) {
|
||||
this._overlayInvokerNode.removeEventListener('click', this.__toggle);
|
||||
}
|
||||
}
|
||||
|
||||
__toggle() {
|
||||
this.opened = !this.opened;
|
||||
}
|
||||
|
||||
// ********************* GESTURE ***********************
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.useOverlay = false;
|
||||
this.useOverlayMediaQuery = '(max-width: 1024px)';
|
||||
|
||||
this.__toggle = this.__toggle.bind(this);
|
||||
|
||||
this.onMatchMedia = this.onMatchMedia.bind(this);
|
||||
this.onGestureStart = this.onGestureStart.bind(this);
|
||||
this.onGestureMove = this.onGestureMove.bind(this);
|
||||
this.onGestureEnd = this.onGestureEnd.bind(this);
|
||||
this.updateFromTouch = this.updateFromTouch.bind(this);
|
||||
|
||||
this.mediaMatcher = window.matchMedia(this.useOverlayMediaQuery);
|
||||
this.mediaMatcher.addEventListener('change', this.onMatchMedia);
|
||||
|
||||
this._startX = 0;
|
||||
this._currentX = 0;
|
||||
this._velocity = 0;
|
||||
this._left = 0;
|
||||
this.__touching = false;
|
||||
this._timestamp = 0;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.useOverlay = !!this.mediaMatcher.matches;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<slot name="invoker"></slot>
|
||||
<slot name="_overlay-shadow-outlet"></slot>
|
||||
<div id="overlay-content-node-wrapper">
|
||||
<slot name="content"></slot>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TouchEvent} ev
|
||||
*/
|
||||
onGestureStart(ev) {
|
||||
if (!this.containerEl) {
|
||||
return;
|
||||
}
|
||||
this.__touching = true;
|
||||
this._left = this.containerEl.getBoundingClientRect().left;
|
||||
this._startX = ev.targetTouches[0].clientX;
|
||||
this._currentX = this._startX;
|
||||
this._timestamp = new Date().getTime();
|
||||
this._velocity = 0;
|
||||
|
||||
this._overlayCtrl.contentNode.style.transition = '';
|
||||
|
||||
document.body.addEventListener('touchmove', this.onGestureMove, { passive: true });
|
||||
document.body.addEventListener('touchend', this.onGestureEnd, { passive: true });
|
||||
document.body.addEventListener('touchcancel', this.onGestureEnd, { passive: true });
|
||||
requestAnimationFrame(this.updateFromTouch);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} dDist
|
||||
* @param {number} dTime
|
||||
*/
|
||||
addVelocitySample(dDist, dTime) {
|
||||
if (dTime === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const velocitySample = dDist / dTime;
|
||||
|
||||
// Low pass filter.
|
||||
const alpha = 0.75;
|
||||
this._velocity *= alpha;
|
||||
this._velocity += (1 - alpha) * velocitySample;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TouchEvent} ev
|
||||
*/
|
||||
onGestureMove(ev) {
|
||||
if (!this.__touching) {
|
||||
return;
|
||||
}
|
||||
const lastTimestamp = this._timestamp;
|
||||
this._timestamp = new Date().getTime();
|
||||
const dTime = this._timestamp - lastTimestamp;
|
||||
const lastX = this._currentX;
|
||||
this._currentX = ev.targetTouches[0].clientX;
|
||||
const dX = this._currentX - lastX;
|
||||
this.addVelocitySample(dX, dTime);
|
||||
}
|
||||
|
||||
onGestureEnd() {
|
||||
if (!this.__touching || !this.containerEl) {
|
||||
this.opened = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.__touching = false;
|
||||
let endOpenedState;
|
||||
|
||||
// Check for fling.
|
||||
if (Math.abs(this._velocity) > 1) {
|
||||
endOpenedState = this._velocity > 0;
|
||||
} else {
|
||||
// Check depending on percentage visible.
|
||||
const { left } = this.containerEl.getBoundingClientRect();
|
||||
const width = this.containerEl.clientWidth;
|
||||
const percentageVisible = (left + width) / width;
|
||||
endOpenedState = percentageVisible >= 0.5;
|
||||
}
|
||||
|
||||
this._overlayCtrl.contentNode.style.transition =
|
||||
'transform 0.20s cubic-bezier(0.4, 0.0, 0.2, 1)';
|
||||
|
||||
this.containerEl.style.transform = '';
|
||||
this.opened = endOpenedState;
|
||||
|
||||
document.body.removeEventListener('touchmove', this.onGestureMove);
|
||||
document.body.removeEventListener('touchend', this.onGestureEnd);
|
||||
document.body.removeEventListener('touchcancel', this.onGestureEnd);
|
||||
}
|
||||
|
||||
updateFromTouch() {
|
||||
if (!this.__touching || !this.containerEl) {
|
||||
return;
|
||||
}
|
||||
requestAnimationFrame(this.updateFromTouch);
|
||||
|
||||
const translateX = Math.min(0, this._currentX - this._startX + this._left);
|
||||
this.containerEl.style.transform = `translateX(${translateX}px)`;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
// Don't edit this file directly. It is generated by /scripts/update-package-configs.ts
|
||||
|
||||
{
|
||||
"extends": "../../tsconfig.browser-base.json",
|
||||
"compilerOptions": {
|
||||
"module": "ESNext",
|
||||
"outDir": "./dist-types",
|
||||
"rootDir": ".",
|
||||
"composite": true,
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"emitDeclarationOnly": true
|
||||
},
|
||||
"references": [],
|
||||
"include": [
|
||||
"src",
|
||||
"*.js",
|
||||
"types"
|
||||
],
|
||||
"exclude": [
|
||||
"dist",
|
||||
"dist-types"
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,81 @@
|
||||
# @rocket/engine
|
||||
|
||||
## 0.2.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 0ed3d6d: Adjust urls containing url fragments
|
||||
|
||||
```html
|
||||
<!-- user writes -->
|
||||
<a href="./about.rocket.js#some-id"></a>
|
||||
|
||||
<!-- rocket outputs -->
|
||||
<!-- before -->
|
||||
<a href="./about.rocket.js#some-id"></a>
|
||||
<!-- after -->
|
||||
<a href="/about/#some-id"></a>
|
||||
```
|
||||
|
||||
## 0.2.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 8dedc56: Add `engine.getVersion()` method
|
||||
- 390335d: Improve title tag handling
|
||||
|
||||
## 0.2.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 93503ed: HTML in headings will be ignored for the menu
|
||||
Some examples:
|
||||
|
||||
- `<h1>Hello <em>Word</em></h1>` => `Hello Word`
|
||||
- `<h1>Hello <strong>World</strong> of <span>JS <em>(JavaScript)</em></span>!</h1>` => `Hello World of JS (JavaScript)!`
|
||||
|
||||
- 3032ba9: Menus now support special characters in markdown headings.
|
||||
|
||||
Examples:
|
||||
|
||||
```md
|
||||
# Fun Errors & Feedback
|
||||
|
||||
# <some-button>
|
||||
```
|
||||
|
||||
## 0.2.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 09a47b4: Prevent fatal error because of simultaneous write to file.
|
||||
|
||||
When the browser requested a file to be rendered and that file also needed an update in the "rocket header" (the top of the file) then it could be that the watcher trigger a simultaneous render of the file while the first render was still in progress.
|
||||
|
||||
The solution is that the watcher ignores changes to a file until a full render is finished.
|
||||
|
||||
## 0.2.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 379f08f: Remove the lit workaround to globally load the `global-dom-shim` in the "main thread".
|
||||
Which means only the worker that does the actual SSR rendering will load it.
|
||||
|
||||
## 0.2.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 6f88d8e: Get rid of the `rehype-prism` workaround by using latest esm version of mdjs that uses `rehype-prism-plus`
|
||||
- Updated dependencies [35ed64d]
|
||||
- Updated dependencies [6f88d8e]
|
||||
- @mdjs/core@0.20.0
|
||||
|
||||
## 0.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 367529c: Make sure user provided content in the folder `site/public/*` wins over public folders content provided by plugins.
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@rocket/engine",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.7",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
@@ -35,9 +35,9 @@
|
||||
"scripts": {
|
||||
"debug": "DEBUG=engine:rendering yarn test",
|
||||
"debug:integration": "PWDEBUG=1 yarn test:integration",
|
||||
"test": "mocha --require ../../scripts/testMochaGlobalHooks.js --timeout 5000 test-node/**/*.test.{js,cjs} test-node/*.test.{js,cjs}",
|
||||
"test:integration": "playwright test test-node/*.spec.js",
|
||||
"test:watch": "onchange 'src/**/*.{js,cjs}' 'test-node/**/*.{js,cjs}' -- npm test",
|
||||
"test": "mocha --require ../../scripts/testMochaGlobalHooks.js --timeout 8000 test-node/**/*.test.js test-node/*.test.js",
|
||||
"test:integration": "playwright test test-node/*.spec.js --retries=3",
|
||||
"test:watch": "onchange 'src/**/*.js' 'test-node/**/*.js' -- npm test",
|
||||
"types:copy": "copyfiles \"./types/**/*.d.ts\" dist-types/"
|
||||
},
|
||||
"files": [
|
||||
@@ -47,14 +47,15 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@d4kmor/tree-model": "^0.1.3",
|
||||
"@lit-labs/ssr": "^2.0.4",
|
||||
"@mdjs/core": "^0.9.5",
|
||||
"@lit-labs/ssr": "^2.2.3",
|
||||
"@mdjs/core": "^0.20.0",
|
||||
"@parcel/watcher": "^2.0.5",
|
||||
"@web/dev-server": "^0.1.4",
|
||||
"es-module-lexer": "^0.9.3",
|
||||
"lit": "^2.2.5",
|
||||
"es-module-lexer": "^0.10.5",
|
||||
"lit": "^2.3.0",
|
||||
"plugins-manager": "^0.3.0",
|
||||
"sax-wasm": "^2.1.3"
|
||||
"sax-wasm": "^2.1.3",
|
||||
"unist-util-visit": "^4.1.0"
|
||||
},
|
||||
"types": "./dist-types/src/index.d.ts",
|
||||
"typesVersions": {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { existsSync } from 'fs';
|
||||
// TODO: implement copy without extra dependency => node 16.7.0 copy has recursive
|
||||
import fse from 'fs-extra';
|
||||
import { mkdir, rm } from 'fs/promises';
|
||||
import { mkdir, readFile, rm } from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { EventEmitter } from 'events';
|
||||
import { startDevServer } from '@web/dev-server';
|
||||
@@ -32,6 +32,8 @@ import { RocketHeader } from './file-header/RocketHeader.js';
|
||||
|
||||
const logRendering = debuglog('engine:rendering');
|
||||
|
||||
const pkgJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
|
||||
|
||||
export class Engine {
|
||||
/** @type {EngineOptions} */
|
||||
options = {
|
||||
@@ -121,8 +123,10 @@ export class Engine {
|
||||
if (pageTree.pageTreeChangedOnSave) {
|
||||
for (const sourceFilePath of sourceFiles) {
|
||||
const result = await this.renderFile({ sourceFilePath, throwOnError: true });
|
||||
await pageTree.add(result.sourceRelativeFilePath);
|
||||
await cleanupAutoGeneratedFiles(result);
|
||||
}
|
||||
await pageTree.save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,12 +156,7 @@ export class Engine {
|
||||
* @param {string} targetDir
|
||||
*/
|
||||
async copyPublicFilesTo(targetDir) {
|
||||
// copy public files
|
||||
const publicDir = path.join(this.docsDir, '..', 'public');
|
||||
if (existsSync(publicDir)) {
|
||||
await fse.copy(publicDir, targetDir);
|
||||
}
|
||||
// copy public files of plugins
|
||||
// 1. copy public files of plugins
|
||||
if (this.options.plugins) {
|
||||
for (const plugin of this.options.plugins) {
|
||||
// @ts-ignore
|
||||
@@ -171,6 +170,12 @@ export class Engine {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. copy public files from inputDir (e.g. user public folder always wins)
|
||||
const publicDir = path.join(this.docsDir, '..', 'public');
|
||||
if (existsSync(publicDir)) {
|
||||
await fse.copy(publicDir, targetDir);
|
||||
}
|
||||
}
|
||||
|
||||
async start(options = {}) {
|
||||
@@ -264,6 +269,7 @@ export class Engine {
|
||||
readFileConfig: false,
|
||||
// argv: this.__argv,
|
||||
});
|
||||
this.events.emit('devServerStarted');
|
||||
|
||||
this.devServer.webSockets.on(
|
||||
'message',
|
||||
@@ -443,7 +449,9 @@ export class Engine {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (this.watcher) {
|
||||
this.watcher.addFileToIgnore(sourceFilePath);
|
||||
}
|
||||
if (rocketHeader) {
|
||||
const { needsAnotherRenderingPass } = await rocketHeader.syncComponents({
|
||||
outputFileContent: result.fileContent,
|
||||
@@ -459,6 +467,13 @@ export class Engine {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.watcher) {
|
||||
this.watcher.removeFileToIgnore(sourceFilePath);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
getVersion() {
|
||||
return pkgJson.version;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,11 @@ export class Watcher {
|
||||
|
||||
acceptPageUpdates = true;
|
||||
|
||||
/**
|
||||
* @type {Set<string>}
|
||||
*/
|
||||
_filesToIgnore = new Set();
|
||||
|
||||
/**
|
||||
* @type {Map<string, { type: string, jsDependencies?: string[], webSockets?: Set<import('@web/dev-server-core').WebSocket> }>}
|
||||
*/
|
||||
@@ -96,6 +101,9 @@ export class Watcher {
|
||||
async (err, events) => {
|
||||
if (this.acceptPageUpdates) {
|
||||
for (const event of events) {
|
||||
if (this.isIgnoredFile(event.path)) {
|
||||
return;
|
||||
}
|
||||
if (event.type === 'create') {
|
||||
await this.addCreateTask(event.path);
|
||||
}
|
||||
@@ -109,6 +117,9 @@ export class Watcher {
|
||||
await this.executeTaskQueue();
|
||||
} else {
|
||||
for (const event of events) {
|
||||
if (this.isIgnoredFile(event.path)) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
this._taskQueue.has(event.path) ||
|
||||
// we exclude files here as `@parcel/watcher` does not support globs in `ignore`
|
||||
@@ -340,4 +351,26 @@ export class Watcher {
|
||||
this._taskQueue.clear();
|
||||
this.acceptPageUpdates = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} filePath
|
||||
*/
|
||||
addFileToIgnore(filePath) {
|
||||
this._filesToIgnore.add(filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} filePath
|
||||
*/
|
||||
removeFileToIgnore(filePath) {
|
||||
this._filesToIgnore.delete(filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} filePath
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isIgnoredFile(filePath) {
|
||||
return this._filesToIgnore.has(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,8 @@ export function devServerAdjustAssetUrls({
|
||||
const outputFilePath = getOutputFilePath(sourceFilePath);
|
||||
const sourceRelativeFilePath = path.relative(inputDir, sourceFilePath);
|
||||
const outputRelativeFilePath = path.relative(outputDir, outputFilePath);
|
||||
const newBody = await adjustAssetUrl.transform(context.body, {
|
||||
const body = /** @type {string} */ (context.body);
|
||||
const newBody = await adjustAssetUrl.transform(body, {
|
||||
sourceFilePath,
|
||||
sourceRelativeFilePath,
|
||||
outputFilePath,
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
// we load this before the global-dom-shim as otherwise prism thinks it's running in a browser 🙈
|
||||
// we need to load the global-dom-shim as otherwise import { html } from 'lit'; breaks
|
||||
import 'rehype-prism';
|
||||
import '@lit-labs/ssr/lib/install-global-dom-shim.js';
|
||||
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
// @ts-ignore
|
||||
import { mdjsProcess } from '@mdjs/core';
|
||||
import { existsSync } from 'fs';
|
||||
import { readFile, writeFile } from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { addPlugin } from 'plugins-manager';
|
||||
import markdown from 'remark-parse';
|
||||
import visit from 'unist-util-visit';
|
||||
import { visit } from 'unist-util-visit';
|
||||
|
||||
/**
|
||||
* @param {string} string
|
||||
@@ -22,7 +18,7 @@ function escapeRegExp(string) {
|
||||
|
||||
const REGEX_REPLACE_ESCAPES = new RegExp(
|
||||
escapeRegExp(
|
||||
'\\\\</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>',
|
||||
'\\\\<span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>',
|
||||
),
|
||||
'g',
|
||||
);
|
||||
@@ -145,7 +141,7 @@ export async function mdInJsToMdHtmlInJs(toImportFilePath) {
|
||||
// this corrects it - escaped
|
||||
mdHTML = mdHTML.replace(
|
||||
REGEX_REPLACE_ESCAPES,
|
||||
'</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">\\\\${</span>',
|
||||
'<span class="token interpolation"><span class="token interpolation-punctuation punctuation">\\\\${</span>',
|
||||
);
|
||||
return [mdHTML, ...mdjsScriptTag].join('\n');
|
||||
}
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
// we load this before the global-dom-shim as otherwise prism thinks it's running in a browser 🙈
|
||||
// we need to load the global-dom-shim as otherwise import { html } from 'lit'; breaks
|
||||
import 'rehype-prism';
|
||||
import '@lit-labs/ssr/lib/install-global-dom-shim.js';
|
||||
|
||||
export { renderJoiningGroup } from './helpers/renderJoiningGroup.js';
|
||||
export { inlineFile } from './helpers/inlineFile.js';
|
||||
|
||||
|
||||
@@ -19,32 +19,39 @@ import { parser, SaxEventType } from '../web-menu/sax-parser.js';
|
||||
* @returns
|
||||
*/
|
||||
async function defaultAdjustAssetUrl({
|
||||
url,
|
||||
url: fullUrl,
|
||||
sourceFilePath,
|
||||
sourceRelativeFilePath,
|
||||
outputFilePath,
|
||||
}) {
|
||||
let url = fullUrl;
|
||||
let fragment = '';
|
||||
if (!url.startsWith('resolve:#') && url.includes('#')) {
|
||||
const urlParts = url.split('#');
|
||||
url = urlParts[0];
|
||||
fragment = `#${urlParts[1]}`;
|
||||
}
|
||||
if (url.startsWith('resolve:')) {
|
||||
const bareImport = url.substring(8);
|
||||
const requireOfSource = createRequire(sourceFilePath);
|
||||
const resolvedPath = requireOfSource.resolve(bareImport);
|
||||
const rel = path.relative(path.dirname(outputFilePath), resolvedPath);
|
||||
return rel;
|
||||
return rel + fragment;
|
||||
}
|
||||
if (url.match(/^[a-z]+:/) || url.startsWith('//')) {
|
||||
return url;
|
||||
return url + fragment;
|
||||
}
|
||||
if (isRocketPageFile(url)) {
|
||||
const dir = path.dirname(sourceRelativeFilePath);
|
||||
return sourceRelativeFilePathToUrl(path.join(dir, url));
|
||||
return sourceRelativeFilePathToUrl(path.join(dir, url)) + fragment;
|
||||
}
|
||||
if (url.startsWith('./') || url.startsWith('../')) {
|
||||
return path.relative(
|
||||
path.dirname(outputFilePath),
|
||||
path.join(path.dirname(sourceFilePath), url),
|
||||
return (
|
||||
path.relative(path.dirname(outputFilePath), path.join(path.dirname(sourceFilePath), url)) +
|
||||
fragment
|
||||
);
|
||||
}
|
||||
return url;
|
||||
return url + fragment;
|
||||
}
|
||||
|
||||
export class AdjustAssetUrls {
|
||||
|
||||
@@ -28,10 +28,28 @@ export function getHtmlMetaData(htmlFilePath) {
|
||||
const metaData = {
|
||||
// headlinesWithId: [],
|
||||
};
|
||||
|
||||
/** @type {string | undefined} */
|
||||
let capturedHeadlineText = undefined;
|
||||
let withinHTMLHead = false;
|
||||
parser.eventHandler = (ev, _data) => {
|
||||
if (ev === SaxEventType.OpenTag) {
|
||||
const data = /** @type {Tag} */ (/** @type {any} */ (_data));
|
||||
if (isHeadline(data)) {
|
||||
capturedHeadlineText = '';
|
||||
}
|
||||
if (data.name === 'head') {
|
||||
withinHTMLHead = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (capturedHeadlineText !== undefined && ev === SaxEventType.Text) {
|
||||
const data = /** @type {Text} */ (/** @type {any} */ (_data));
|
||||
capturedHeadlineText += data.value;
|
||||
}
|
||||
|
||||
if (ev === SaxEventType.CloseTag) {
|
||||
const data = /** @type {Tag} */ (/** @type {any} */ (_data));
|
||||
// ********** <meta name="*" content="*">
|
||||
if (data.name === 'meta') {
|
||||
const metaName = getAttribute(data, 'name');
|
||||
if (metaName === 'menu:link.text') {
|
||||
@@ -60,29 +78,39 @@ export function getHtmlMetaData(htmlFilePath) {
|
||||
metaData.menuNoLink = getAttribute(data, 'content') !== 'false';
|
||||
}
|
||||
}
|
||||
if (!metaData.title && data.name === 'title') {
|
||||
if (withinHTMLHead && data.name === 'title') {
|
||||
metaData.title = getText(data);
|
||||
}
|
||||
if (!metaData.h1 && data.name === 'h1') {
|
||||
metaData.h1 = getText(data);
|
||||
}
|
||||
|
||||
// ********** <h1> - <h6>
|
||||
if (isHeadline(data)) {
|
||||
const id = getAttribute(data, 'id');
|
||||
const rawText = getText(data);
|
||||
const linkText = getAttribute(data, 'link-text');
|
||||
if (id && rawText) {
|
||||
const processedCapturedHeadlineText = capturedHeadlineText
|
||||
?.replace(/</g, '<')
|
||||
.replace(/&/g, '&')
|
||||
.trim();
|
||||
const text = linkText || processedCapturedHeadlineText || '';
|
||||
if (!metaData.h1 && data.name === 'h1') {
|
||||
metaData.h1 = text;
|
||||
}
|
||||
if (id && text) {
|
||||
if (!metaData.headlinesWithId) {
|
||||
metaData.headlinesWithId = [];
|
||||
}
|
||||
const rawTextObj = linkText ? { rawText } : {};
|
||||
const rawTextObj = linkText ? { rawText: processedCapturedHeadlineText } : {};
|
||||
metaData.headlinesWithId.push({
|
||||
text: linkText || rawText,
|
||||
text,
|
||||
id,
|
||||
level: parseInt(data.name[1], 10),
|
||||
...rawTextObj,
|
||||
});
|
||||
}
|
||||
capturedHeadlineText = undefined;
|
||||
}
|
||||
|
||||
if (data.name === 'head') {
|
||||
withinHTMLHead = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -97,6 +125,7 @@ export function getHtmlMetaData(htmlFilePath) {
|
||||
});
|
||||
readable.on('end', () => {
|
||||
parser.end();
|
||||
capturedHeadlineText = undefined;
|
||||
|
||||
resolve(metaData);
|
||||
});
|
||||
|
||||
@@ -9,6 +9,9 @@ const require = createRequire(import.meta.url);
|
||||
export const streamOptions = { highWaterMark: 128 * 1024 };
|
||||
const saxPath = require.resolve('sax-wasm/lib/sax-wasm.wasm');
|
||||
const saxWasmBuffer = await readFile(saxPath);
|
||||
export const parser = new SAXParser(SaxEventType.CloseTag, streamOptions);
|
||||
export const parser = new SAXParser(
|
||||
SaxEventType.OpenTag | SaxEventType.CloseTag | SaxEventType.Text,
|
||||
streamOptions,
|
||||
);
|
||||
|
||||
await parser.prepareWasm(saxWasmBuffer);
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
// we load this before the global-dom-shim as otherwise prism thinks it's running in a browser 🙈
|
||||
// we need to load the global-dom-shim as otherwise import { html } from 'lit'; breaks
|
||||
import 'rehype-prism';
|
||||
import '@lit-labs/ssr/lib/install-global-dom-shim.js';
|
||||
|
||||
import { parentPort } from 'worker_threads';
|
||||
// import { convertMdFile, convertHtmlFile } from '../converts.js';
|
||||
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
// we load this before the global-dom-shim as otherwise prism thinks it's running in a browser 🙈
|
||||
// we need to load the global-dom-shim as otherwise import { html } from 'lit'; breaks
|
||||
import 'rehype-prism';
|
||||
import '@lit-labs/ssr/lib/install-global-dom-shim.js';
|
||||
|
||||
import path from 'path';
|
||||
import { parentPort } from 'worker_threads';
|
||||
import { mkdir, writeFile } from 'fs/promises';
|
||||
|
||||
@@ -72,10 +72,10 @@ describe('Format Markdown', () => {
|
||||
'<p>Escape JS</p>',
|
||||
'<pre',
|
||||
' class="language-js"',
|
||||
'><code class="language-js"><span class="token keyword">const</span> foo <span class="token operator">=</span> <span class="token string">\'one\'</span><span class="token punctuation">;</span>',
|
||||
'<span class="token keyword">const</span> bar <span class="token operator">=</span> html<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"><p>${foo}</p></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>',
|
||||
'<span class="token keyword">const</span> baz <span class="token operator">=</span> html<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"><span></span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">\\${</span>foo<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"></span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>',
|
||||
'</code></pre>',
|
||||
'><code class="language-js code-highlight"><span class="code-line"><span class="token keyword">const</span> foo <span class="token operator">=</span> <span class="token string">\'one\'</span><span class="token punctuation">;</span>',
|
||||
'</span><span class="code-line"><span class="token keyword">const</span> bar <span class="token operator">=</span> html<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token html language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>${foo}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>',
|
||||
'</span><span class="code-line"><span class="token keyword">const</span> baz <span class="token operator">=</span> html<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token html language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">\\${</span>foo<span class="token interpolation-punctuation punctuation">}</span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>',
|
||||
'</span></code></pre>',
|
||||
].join('\n'),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import chai from 'chai';
|
||||
// import '@lit-labs/ssr/lib/install-global-dom-shim.js';
|
||||
import { html } from 'lit';
|
||||
import { renderJoiningGroup } from '../src/helpers/renderJoiningGroup.js';
|
||||
import { testLitServerRender } from './test-helpers.js';
|
||||
|
||||
@@ -834,4 +834,118 @@ describe('Engine menus', () => {
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
it('14: get-all-text-but-strip-html', async () => {
|
||||
const { build, readSource } = await setupTestEngine(
|
||||
'fixtures/05-menu/14-get-all-text-but-strip-html/docs',
|
||||
);
|
||||
await build();
|
||||
|
||||
expect(JSON.parse(readSource('pageTreeData.rocketGenerated.json'))).to.deep.equal({
|
||||
h1: 'Hello World of JS (JavaScript)!',
|
||||
name: 'Hello World of JS (JavaScript)!',
|
||||
menuLinkText: 'Hello World of JS (JavaScript)!',
|
||||
url: '/',
|
||||
outputRelativeFilePath: 'index.html',
|
||||
sourceRelativeFilePath: 'index.rocket.js',
|
||||
level: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('15: markdown special characters', async () => {
|
||||
const { build, readSource } = await setupTestEngine(
|
||||
'fixtures/05-menu/15-md-special-characters/docs',
|
||||
);
|
||||
await build();
|
||||
|
||||
expect(JSON.parse(readSource('pageTreeData.rocketGenerated.json'))).to.deep.equal({
|
||||
children: [
|
||||
{
|
||||
h1: '<some-button>',
|
||||
headlinesWithId: [
|
||||
{
|
||||
id: 'some-button',
|
||||
level: 1,
|
||||
text: '<some-button>',
|
||||
},
|
||||
],
|
||||
level: 1,
|
||||
menuLinkText: '<some-button>',
|
||||
name: '<some-button>',
|
||||
outputRelativeFilePath: 'component/index.html',
|
||||
sourceRelativeFilePath: 'component.rocket.md',
|
||||
url: '/component/',
|
||||
},
|
||||
],
|
||||
h1: 'Fun Errors & Feedback',
|
||||
headlinesWithId: [
|
||||
{
|
||||
id: 'fun-errors--feedback',
|
||||
level: 1,
|
||||
text: 'Fun Errors & Feedback',
|
||||
},
|
||||
],
|
||||
level: 0,
|
||||
menuLinkText: 'Fun Errors & Feedback',
|
||||
name: 'Fun Errors & Feedback',
|
||||
outputRelativeFilePath: 'index.html',
|
||||
sourceRelativeFilePath: 'index.rocket.md',
|
||||
url: '/',
|
||||
});
|
||||
});
|
||||
|
||||
it('16: link-text attribute', async () => {
|
||||
const { build, readSource } = await setupTestEngine(
|
||||
'fixtures/05-menu/16-link-text-attribute/docs',
|
||||
);
|
||||
await build();
|
||||
|
||||
expect(JSON.parse(readSource('pageTreeData.rocketGenerated.json'))).to.deep.equal({
|
||||
h1: 'Home',
|
||||
headlinesWithId: [
|
||||
{
|
||||
id: 'home',
|
||||
level: 1,
|
||||
rawText: 'Welcome to Rocket',
|
||||
text: 'Home',
|
||||
},
|
||||
{
|
||||
id: 'first',
|
||||
level: 2,
|
||||
text: 'First',
|
||||
},
|
||||
{
|
||||
id: 'second',
|
||||
level: 2,
|
||||
rawText: 'Second is best',
|
||||
text: 'Second',
|
||||
},
|
||||
],
|
||||
level: 0,
|
||||
menuLinkText: 'Home',
|
||||
name: 'Home',
|
||||
outputRelativeFilePath: 'index.html',
|
||||
sourceRelativeFilePath: 'index.rocket.js',
|
||||
url: '/',
|
||||
});
|
||||
});
|
||||
|
||||
it('17: title-tag', async () => {
|
||||
const { build, readSource, deleteSource } = await setupTestEngine(
|
||||
'fixtures/05-menu/17-title-tag/docs',
|
||||
);
|
||||
await deleteSource('pageTreeData.rocketGenerated.json');
|
||||
await build();
|
||||
|
||||
expect(JSON.parse(readSource('pageTreeData.rocketGenerated.json'))).to.deep.equal({
|
||||
h1: 'Welcome to Rocket',
|
||||
level: 0,
|
||||
menuLinkText: 'Welcome to Rocket',
|
||||
name: 'Welcome to Rocket',
|
||||
outputRelativeFilePath: 'index.html',
|
||||
sourceRelativeFilePath: 'index.rocket.js',
|
||||
title: 'Welcome to Rocket | Rocket',
|
||||
url: '/',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import chai from 'chai';
|
||||
import { AdjustAssetUrls } from '@rocket/engine';
|
||||
import { expectThrowsAsync } from './test-helpers.js';
|
||||
|
||||
const { expect } = chai;
|
||||
|
||||
@@ -48,7 +49,7 @@ describe('AdjustAssetUrls', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('ignores <a href="#foo"></a>', async () => {
|
||||
it('does not adjust <a href="#foo"></a>', async () => {
|
||||
const adjust = new AdjustAssetUrls();
|
||||
expect(await adjust.transform('<a href="#foo">go</a>', options)).to.equal(
|
||||
'<a href="#foo">go</a>',
|
||||
@@ -104,4 +105,43 @@ describe('AdjustAssetUrls', () => {
|
||||
}),
|
||||
).to.equal('<a href="/">go</a>');
|
||||
});
|
||||
|
||||
it('adjust <a href="./about.rocket.js#some-id"></a>', async () => {
|
||||
const adjust = new AdjustAssetUrls();
|
||||
expect(await adjust.transform('<a href="./about.rocket.js#some-id">go</a>', options)).to.equal(
|
||||
'<a href="/about/#some-id">go</a>',
|
||||
);
|
||||
expect(
|
||||
await adjust.transform('<a href="./about.rocket.js#some-id">go</a>', {
|
||||
sourceRelativeFilePath: 'components/index.rocket.js',
|
||||
outputFilePath: '/my/path/to/__output/components/index.html',
|
||||
}),
|
||||
).to.equal('<a href="/components/about/#some-id">go</a>');
|
||||
|
||||
expect(
|
||||
await adjust.transform('<a href="./about.rocket.js#some-id">go</a>', {
|
||||
sourceRelativeFilePath: 'components.rocket.js',
|
||||
outputFilePath: '/my/path/to/__output/components/index.html',
|
||||
}),
|
||||
).to.equal('<a href="/about/#some-id">go</a>');
|
||||
|
||||
expect(
|
||||
await adjust.transform('<a href="./index.rocket.js#some-id">go</a>', {
|
||||
sourceRelativeFilePath: 'about.rocket.js',
|
||||
outputFilePath: '/my/path/to/__output/about/index.html',
|
||||
}),
|
||||
).to.equal('<a href="/#some-id">go</a>');
|
||||
});
|
||||
|
||||
it('still resolves private imports <img src="resolve:#src/logo.svg" />', async () => {
|
||||
const adjust = new AdjustAssetUrls();
|
||||
// we check for the resolve throw as this private import does not exists =>
|
||||
// which means if a not resolve related error in our code happens the test fails
|
||||
await expectThrowsAsync(
|
||||
() => adjust.transform('<img src="resolve:#src/logo.svg" />', options),
|
||||
{
|
||||
errorMatch: /Cannot find module '#src\/logo\.svg'/,
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -114,4 +114,77 @@ describe('Engine start error handling', () => {
|
||||
);
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
it('04: update-header-while-rendering', async () => {
|
||||
const {
|
||||
readOutput,
|
||||
writeSource,
|
||||
cleanup,
|
||||
engine,
|
||||
setAsOpenedInBrowser,
|
||||
outputExists,
|
||||
anEngineEvent,
|
||||
} = await setupTestEngine(
|
||||
'fixtures/09b-watch-error-handling/04-update-header-while-rendering/docs',
|
||||
);
|
||||
expect(outputExists('index.html')).to.be.false;
|
||||
|
||||
await engine.start();
|
||||
setAsOpenedInBrowser('index.rocket.js');
|
||||
await writeSource(
|
||||
'index.rocket.js',
|
||||
[
|
||||
'/* START - Rocket auto generated - do not touch */',
|
||||
"export const sourceRelativeFilePath = 'index.rocket.js';",
|
||||
"import { html, components, layout } from './recursive.data.js';",
|
||||
'export { html, components, layout };',
|
||||
'export async function registerCustomElements() {',
|
||||
' // hydrate-able components',
|
||||
" customElements.define('hello-typer', await import('#c/HelloTyper.js').then(m => m.HelloTyper));",
|
||||
'}',
|
||||
'export const needsLoader = true;',
|
||||
'/* END - Rocket auto generated - do not touch */',
|
||||
'',
|
||||
'export default () => html`',
|
||||
' <h1>Hello World</h1>',
|
||||
' <hello-typer loading="hydrate:onVisible"></hello-typer>',
|
||||
'`;',
|
||||
].join('\n'),
|
||||
);
|
||||
await anEngineEvent('rocketUpdated');
|
||||
|
||||
expect(readOutput('index.html')).to.equal(
|
||||
[
|
||||
'<!DOCTYPE html>',
|
||||
'<html lang="en">',
|
||||
' <head>',
|
||||
' <meta charset="utf-8" />',
|
||||
' </head>',
|
||||
' <body>',
|
||||
' <h1>Hello World</h1>',
|
||||
' <hello-typer loading="hydrate:onVisible"',
|
||||
' ><template shadowroot="open"',
|
||||
' ><style>',
|
||||
' button {',
|
||||
' font-size: 200%;',
|
||||
' width: 64px;',
|
||||
' height: 64px;',
|
||||
' border: none;',
|
||||
' border-radius: 10px;',
|
||||
' background-color: seagreen;',
|
||||
' color: white;',
|
||||
' }',
|
||||
' </style>',
|
||||
' <p>🤔 Hello <span> </span></p>',
|
||||
' <button>+</button>',
|
||||
' </template></hello-typer',
|
||||
' >',
|
||||
' <script type="module" src="index-loader-generated.js"></script>',
|
||||
' </body>',
|
||||
'</html>',
|
||||
].join('\n'),
|
||||
);
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,23 +4,42 @@ import { setupTestEngine } from './test-helpers.js';
|
||||
|
||||
const { expect } = chai;
|
||||
|
||||
class MyPlugin {
|
||||
static publicFolder = new URL(
|
||||
'./fixtures/10-plugins/01-add-public-files/plugin-add-to-public/preset/__public',
|
||||
import.meta.url,
|
||||
).pathname;
|
||||
}
|
||||
|
||||
describe('Plugins', () => {
|
||||
it('add plugin with custom public files', async () => {
|
||||
it('01: add plugin with custom public files', async () => {
|
||||
class TestPlugin01 {
|
||||
static publicFolder = new URL(
|
||||
'./fixtures/10-plugins/01-add-public-files/plugin-add-to-public/preset/__public',
|
||||
import.meta.url,
|
||||
).pathname;
|
||||
}
|
||||
const { build, outputExists } = await setupTestEngine(
|
||||
'fixtures/10-plugins/01-add-public-files/docs',
|
||||
{
|
||||
setupPlugins: [addPlugin(MyPlugin)],
|
||||
setupPlugins: [addPlugin(TestPlugin01)],
|
||||
},
|
||||
);
|
||||
await build();
|
||||
|
||||
expect(outputExists('added-via-plugin.txt')).to.be.true;
|
||||
});
|
||||
|
||||
it('02: add plugin with custom public files', async () => {
|
||||
class TestPlugin02 {
|
||||
static publicFolder = new URL(
|
||||
'./fixtures/10-plugins/02-input-folder-public-always-wins/plugin-add-to-public/preset/__public',
|
||||
import.meta.url,
|
||||
).pathname;
|
||||
}
|
||||
const { build, readOutput } = await setupTestEngine(
|
||||
'fixtures/10-plugins/02-input-folder-public-always-wins/docs',
|
||||
{
|
||||
setupPlugins: [addPlugin(TestPlugin02)],
|
||||
},
|
||||
);
|
||||
await build();
|
||||
|
||||
expect(readOutput('added-via-plugin-and-input-public.txt')).to.equal(
|
||||
'from input public folder\n',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -179,15 +179,16 @@ test.describe('hydration', async () => {
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
test("55: hydrate onMedia('(min-width: 640px)') || onClick", async ({ page }) => {
|
||||
test("55: hydrate onMedia('(min-width: 640px)') || onClick - on desktop", async ({ page }) => {
|
||||
const { engine, cleanup } = await setupTestEngine(
|
||||
'fixtures/14-components/55-hydration-onMedia-or-onClick/docs',
|
||||
);
|
||||
await engine.start();
|
||||
const { port } = engine.devServer.config;
|
||||
|
||||
// 1. start on small screen
|
||||
await page.setViewportSize({
|
||||
width: 640,
|
||||
width: 320,
|
||||
height: 480,
|
||||
});
|
||||
await page.goto(`localhost:${port}`);
|
||||
@@ -195,25 +196,40 @@ test.describe('hydration', async () => {
|
||||
const hydrated1 = await myEl.getAttribute('hydrated');
|
||||
expect(hydrated1).toBe(null); // not hydrated
|
||||
|
||||
// 2. go bigger
|
||||
await page.setViewportSize({
|
||||
width: 640,
|
||||
height: 480,
|
||||
});
|
||||
await page.waitForLoadState('networkidle0');
|
||||
|
||||
const hydrated2 = await myEl.getAttribute('hydrated');
|
||||
expect(hydrated2).toBe(''); // boolean attribute is there
|
||||
|
||||
// revisit page on "mobile"
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
test("55b: hydrate onMedia('(min-width: 640px)') || onClick - on mobile", async ({ page }) => {
|
||||
const { engine, cleanup } = await setupTestEngine(
|
||||
'fixtures/14-components/55-hydration-onMedia-or-onClick/docs',
|
||||
);
|
||||
await engine.start();
|
||||
const { port } = engine.devServer.config;
|
||||
|
||||
await page.setViewportSize({
|
||||
width: 320,
|
||||
height: 480,
|
||||
});
|
||||
await page.reload();
|
||||
const myEl2 = await page.locator('my-el');
|
||||
const hydrated3 = await myEl2.getAttribute('hydrated');
|
||||
await page.goto(`localhost:${port}`);
|
||||
const myEl = await page.locator('my-el');
|
||||
|
||||
const hydrated3 = await myEl.getAttribute('hydrated');
|
||||
expect(hydrated3).toBe(null); // not hydrated
|
||||
|
||||
await myEl.click();
|
||||
await page.waitForLoadState('networkidle0');
|
||||
|
||||
const hydrated4 = await myEl2.getAttribute('hydrated');
|
||||
const hydrated4 = await myEl.getAttribute('hydrated');
|
||||
expect(hydrated4).toBe(''); // boolean attribute is there
|
||||
|
||||
await cleanup();
|
||||
@@ -243,7 +259,7 @@ test.describe('hydration', async () => {
|
||||
const focusInEv = await myEl.getAttribute('focusin-ev');
|
||||
expect(focusInEv).toBe('');
|
||||
|
||||
// NOTE: focus event is NOT supported as it does not bubble
|
||||
// NOTE: we are using the focusin event as the focus event is NOT supported as it does not bubble
|
||||
await cleanup();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"title": "Fixed Title",
|
||||
"h1": "\n Welcome Members:\n ",
|
||||
"h1": "Welcome Members:",
|
||||
"headlinesWithId": [
|
||||
{
|
||||
"text": "\n Welcome Members:\n ",
|
||||
"text": "Welcome Members:",
|
||||
"id": "welcome-members",
|
||||
"level": 1
|
||||
}
|
||||
],
|
||||
"name": "Fixed Title",
|
||||
"menuLinkText": "\n Welcome Members:\n ",
|
||||
"menuLinkText": "Welcome Members:",
|
||||
"url": "/",
|
||||
"outputRelativeFilePath": "index.html",
|
||||
"sourceRelativeFilePath": "index.rocket.js",
|
||||
|
||||
@@ -6,7 +6,13 @@ export { layout };
|
||||
|
||||
import { html } from 'lit';
|
||||
export default () => html`
|
||||
<meta name="menu:link.text" content="About" />
|
||||
<title>About | MyPage</title>
|
||||
<h1>This is About</h1>
|
||||
<html>
|
||||
<head>
|
||||
<title>About | MyPage</title>
|
||||
</head>
|
||||
<body>
|
||||
<meta name="menu:link.text" content="About" />
|
||||
<h1>This is About</h1>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
"level": 0,
|
||||
"children": [
|
||||
{
|
||||
"menuLinkText": "About",
|
||||
"title": "About | MyPage",
|
||||
"menuLinkText": "About",
|
||||
"h1": "This is About",
|
||||
"name": "This is About",
|
||||
"url": "/about/",
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/* START - Rocket auto generated - do not touch */
|
||||
export const sourceRelativeFilePath = 'index.rocket.js';
|
||||
import { layout, html } from './recursive.data.js';
|
||||
export { layout, html };
|
||||
/* END - Rocket auto generated - do not touch */
|
||||
|
||||
export default () =>
|
||||
html`<h1>
|
||||
Hello <strong>World</strong> of <span>JS <em>(JavaScript)</em></span
|
||||
>!
|
||||
</h1>`;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user