Skip to content

Commit

Permalink
feat(scullycontent): add a detectable Id to scully rendering errors (#…
Browse files Browse the repository at this point in the history
…230)

* feat(scullycontent): add a detectable Id to scully rendering errors

This will add a `id="___scully-parsing-error___"` to scully rendering errors, so that it will be
easier to detect those during CI/CD steps.

closes #228

* Update scully-content.component.ts

fix typo
  • Loading branch information
SanderElias authored Jan 24, 2020
1 parent bf046c7 commit 71f8e21
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 12 deletions.
2 changes: 1 addition & 1 deletion projects/scullyio/ng-lib/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@scullyio/ng-lib",
"version": "0.0.13",
"version": "0.0.14",
"repository": {
"type": "GIT",
"url": "https://github.com/scullyio/scully/tree/master/projects/scullyio/ng-lib"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export class ScullyContentComponent implements OnInit, OnDestroy {
@Input() type = 'MD';

elm = this.elmRef.nativeElement as HTMLElement;
mutationSubscription: Subscription;
// mutationSubscription: Subscription;
routes = this.srs.available$.pipe(take(1)).toPromise();

constructor(
Expand All @@ -53,10 +53,15 @@ export class ScullyContentComponent implements OnInit, OnDestroy {
/** make sure the idle-check is loaded. */
this.idle.init();
if (this.elm) {
/** this will only fire in a browser environment */
this.handlePage();
}
}

/**
* Loads the static content from scully into the view
* Will fetch the content from sibling links with xmlHTTPrequest
*/
private async handlePage() {
const template = document.createElement('template');
// tslint:disable-next-line: no-string-literal
Expand All @@ -66,17 +71,25 @@ export class ScullyContentComponent implements OnInit, OnDestroy {
template.innerHTML = window['scullyContent'];
} else {
const curPage = location.href;
/**
* NOTE
* when updateting the texts for the errors, make sure you leave the
* `id="___scully-parsing-error___"`
* in there. That way users can detect rendering errors in their CI
* on a reliable way.
*/
await fetchHttp(curPage, 'text')
.then((html: string) => {
try {
template.innerHTML = html.split(scullyBegin)[1].split(scullyEnd)[0];
} catch (e) {
template.innerHTML = `<h2>Sorry, could not parse static page content</h2>
template.innerHTML = `<h2 id="___scully-parsing-error___">Sorry, could not parse static page content</h2>
<p>This might happen if you are not using the static generated pages.</p>`;
}
})
.catch(e => {
template.innerHTML = '<h2>Sorry, could not load static page content</h2>';
template.innerHTML =
'<h2 id="___scully-parsing-error___">Sorry, could not load static page content</h2>';
console.error('problem during loading static scully content', e);
});
}
Expand All @@ -86,24 +99,45 @@ export class ScullyContentComponent implements OnInit, OnDestroy {
parent.insertBefore(begin, this.elm);
parent.insertBefore(template.content, this.elm);
parent.insertBefore(end, this.elm);
/** upgrade all hrefs to simulated routelinks */
document.querySelectorAll('[href]').forEach(this.upgradeToRoutelink.bind(this));
}

/**
* upgrade a **href** attributes to links that respect the Angular router
* and don't do a full page reload. Only works on links that are found in the
* Scully route config file.
* @param elm the element containing the **hrefs**
*/
async upgradeToRoutelink(elm: HTMLElement) {
const routes = await this.routes;
const lnk = elm.getAttribute('href').toLowerCase();
const route = routes.find(r => r.route.toLowerCase() === lnk);
/** only upgrade routes known by scully. */
if (lnk && route) {
elm.onclick = (ev: MouseEvent) => {
elm.onclick = async (ev: MouseEvent) => {
const splitRoute = route.route.split(`/`);
const curSplit = location.pathname.split('/');
// loose last "part" of route
curSplit.pop();

ev.preventDefault();
this.router.navigate(splitRoute).catch(e => console.error('routing error', e));
const routed = await this.router.navigate(splitRoute).catch(e => {
console.error('routing error', e);
return false;
});
if (!routed) {
return;
}
/** delete the content, as it is now out of date! */
window['scullyContent'] = undefined;
/** check for the same route with different "data", and NOT a level higher (length) */
if (curSplit.every((part, i) => splitRoute[i] === part) && splitRoute.length > curSplit.length) {
/**
* as Angular doesn't destroy the component if we stay on the same page,
* we have to manually delete old content. Also we need to kick of loading
* the new content. handlePage() takes care of that.
*/
setTimeout(() => {
const p = this.elm.parentElement;
let cur = findComments(p, 'scullyContent-begin')[0] as HTMLElement;
Expand All @@ -114,21 +148,26 @@ export class ScullyContentComponent implements OnInit, OnDestroy {
cur = next;
} while (next && next !== this.elm);
// tslint:disable-next-line: no-string-literal
window['scullyContent'] = undefined;
this.handlePage();
}, 20);
}, 10); // a small delay, so we are sure the angular parts in the page are settled enough
}
};
}
}

ngOnDestroy() {
if (this.mutationSubscription) {
this.mutationSubscription.unsubscribe();
}
// if (this.mutationSubscription) {
// this.mutationSubscription.unsubscribe();
// }
}
}

/**
* Returns an observable that fires a mutation when the domMutationObserves does that.
* if flattens the mutations to make handling easier, so you only get 1 mutationRecord at a time.
* @param elm the elm to obse with a mutationObserver
* @param config the config for the mutationobserver
*/
export function fromMutationObserver(
elm: HTMLElement,
config: MutationObserverInit
Expand All @@ -140,6 +179,12 @@ export function fromMutationObserver(
});
}

/**
* Returns an array of nodes coninting all the html comments in the element.
* When a searchText is given this is narrowed down to only comments that contian this text
* @param rootElem Element to search nto
* @param searchText optional string that needs to be in a HTML comment
*/
function findComments(rootElem: HTMLElement, searchText?: string) {
const comments = [];
// Fourth argument, which is actually obsolete according to the DOM4 standard, seems required in IE 11
Expand All @@ -150,7 +195,7 @@ function findComments(rootElem: HTMLElement, searchText?: string) {
acceptNode: node => {
// Logic to determine whether to accept, reject or skip node
// In this case, only accept nodes that have content
// other than whitespace
// that is containing our searchText, by rejecting any other nodes.
if (searchText && node.nodeValue && !node.nodeValue.includes(searchText)) {
return NodeFilter.FILTER_REJECT;
}
Expand Down

0 comments on commit 71f8e21

Please sign in to comment.