Compare commits

..

3 Commits

Author SHA1 Message Date
github-actions[bot]
9ae3966fef Version Packages 2022-08-14 22:51:30 +02:00
Thomas Allmer
09a47b43dc fix(engine): prevent fatal error because of simultaneous write to file 2022-08-14 22:48:10 +02:00
Thomas Allmer
42d794bdfc chore: remove drawer pkg & fix contribution link in README.md 2022-08-13 21:40:50 +02:00
20 changed files with 237 additions and 404 deletions

View File

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

View File

@@ -30,7 +30,7 @@
"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",

View File

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

View File

@@ -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).

View File

@@ -1 +0,0 @@
export { RocketDrawer } from './src/RocketDrawer.js';

View File

@@ -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"
}

View File

@@ -1,3 +0,0 @@
import { RocketDrawer } from './src/RocketDrawer.js';
customElements.define('rocket-drawer', RocketDrawer);

View File

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

View File

@@ -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"
]
}

View File

@@ -1,5 +1,15 @@
# @rocket/engine
## 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

View File

@@ -1,6 +1,6 @@
{
"name": "@rocket/engine",
"version": "0.2.3",
"version": "0.2.4",
"publishConfig": {
"access": "public"
},

View File

@@ -444,7 +444,9 @@ export class Engine {
}
return result;
}
if (this.watcher) {
this.watcher.addFileToIgnore(sourceFilePath);
}
if (rocketHeader) {
const { needsAnotherRenderingPass } = await rocketHeader.syncComponents({
outputFileContent: result.fileContent,
@@ -460,6 +462,9 @@ export class Engine {
}
}
if (this.watcher) {
this.watcher.removeFileToIgnore(sourceFilePath);
}
return result;
}
}

View File

@@ -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);
}
}

View File

@@ -114,4 +114,72 @@ describe('Engine start error handling', () => {
);
await cleanup();
});
it('04: update-header-while-rendering', async () => {
const { readOutput, writeSource, cleanup, engine, setAsOpenedInBrowser, outputExists } =
await setupTestEngine(
'fixtures/09b-watch-error-handling/04-update-header-while-rendering/docs',
);
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 engine.start();
setAsOpenedInBrowser('index.rocket.js');
const { port } = engine.devServer.config;
expect(outputExists('index.html')).to.be.false;
await fetch(`http://localhost:${port}/`);
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();
});
});

View File

@@ -0,0 +1,54 @@
import { LitElement, html, css } from 'lit';
let i = 0;
const fullText = [...'to this wonderful world of progressive hydration 🤯'];
export class HelloTyper extends LitElement {
static properties = {
msg: { type: String },
counter: { type: Number },
};
constructor() {
super();
this.msg = ' ';
this.counter = 0;
}
updated(changedProperties) {
super.updated(changedProperties);
if (i < fullText.length) {
setTimeout(() => {
this.msg += fullText[i];
i += 1;
}, Math.floor(Math.random() * 50) + 40);
}
}
render() {
return html`
<p>🤔 Hello <span>${this.msg}</span>${'🤯'.repeat(this.counter)}</p>
<button @click=${this._inc}>+</button>
`;
}
_inc() {
if (i >= fullText.length) {
this.counter += 1;
}
}
static styles = [
css`
button {
font-size: 200%;
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
`,
];
}

View File

@@ -0,0 +1,15 @@
/* 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>
`;

View File

@@ -0,0 +1,22 @@
{
"url": "/",
"outputRelativeFilePath": "index.html",
"sourceRelativeFilePath": "index.rocket.js",
"level": 0,
"children": [
{
"url": "/about/",
"outputRelativeFilePath": "about/index.html",
"sourceRelativeFilePath": "about.rocket.js",
"level": 1
}
],
"menuLinkText": "Hello World",
"name": "Hello World",
"title": "name is not defined",
"h1": "Hello World",
"components": {
"hello-typer": "#c/HelloTyper.js::HelloTyper"
},
"needsLoader": true
}

View File

@@ -0,0 +1,19 @@
import { html } from 'lit';
export { html };
export const components = {
'hello-typer': '#c/HelloTyper.js::HelloTyper',
};
export const layout = data => html`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
</head>
<body>
${data.content()}
</body>
</html>
`;

View File

@@ -0,0 +1,7 @@
{
"name": "@test/components",
"type": "module",
"imports": {
"#c/*": "./components/*"
}
}

View File

@@ -1248,15 +1248,6 @@
"@open-wc/scoped-elements" "^2.1.1"
lit "^2.0.2"
"@lion/overlays@^0.32.0":
version "0.32.0"
resolved "https://registry.yarnpkg.com/@lion/overlays/-/overlays-0.32.0.tgz#d9c7d0bfd7895768efcf9bcc65a97e330bdcf424"
integrity sha512-VyHilarcgWYYM+NDNXxuAiAT8EHipJUHNiaV85OLzwgqqMFC5TsG9wyekqj5iu3mk96R4s/RU1gjqfR9O88INQ==
dependencies:
"@lion/core" "^0.22.0"
"@popperjs/core" "^2.5.4"
singleton-manager "^1.4.3"
"@lit-labs/ssr-client@^1.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@lit-labs/ssr-client/-/ssr-client-1.0.1.tgz#b97e121184aa201bbe6f165f3a7dc919f67129a2"
@@ -1402,11 +1393,6 @@
"@types/node" "*"
playwright-core "1.25.0"
"@popperjs/core@^2.5.4":
version "2.11.6"
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45"
integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
"@rollup/plugin-babel@^5.2.2":
version "5.3.1"
resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283"
@@ -7394,11 +7380,6 @@ signal-exit@^3.0.2:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
singleton-manager@^1.4.3:
version "1.5.0"
resolved "https://registry.yarnpkg.com/singleton-manager/-/singleton-manager-1.5.0.tgz#725827e3c516cd28c1d1a8533fac04a3f2a75e2b"
integrity sha512-38sWGgQlhX4TM9xLHNTdp/qVmD3kGKty8j/d4/xZUL2FLycyCPhYjctWK1gO4FZQa4mc2orPxLmzS5I1QwFMhw==
sinon@^9.2.3:
version "9.2.4"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.2.4.tgz#e55af4d3b174a4443a8762fa8421c2976683752b"