Skip to content
This repository was archived by the owner on Nov 22, 2024. It is now read-only.

feat(aspnet): update engine for angular 5.0 & domino adapter #825

Merged
merged 5 commits into from
Oct 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions modules/aspnetcore-engine/src/interfaces/engine-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { IRequestParams } from "./request-params";
import { Type, NgModuleFactory, StaticProvider } from '@angular/core';

export interface IEngineOptions {
appSelector: string;
request: IRequestParams;
ngModule: Type<{}> | NgModuleFactory<{}>;
providers?: StaticProvider[];
};
appSelector: string; // e.g., <app-root></app-root>
request: IRequestParams; // e.g., params
ngModule: Type<{}> | NgModuleFactory<{}>; // e.g., AppModule
providers?: StaticProvider[]; // StaticProvider[]
}
214 changes: 93 additions & 121 deletions modules/aspnetcore-engine/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,80 @@
import { Type, NgModuleFactory, NgModuleRef, ApplicationRef, CompilerFactory, Compiler } from '@angular/core';
import { platformServer, platformDynamicServer, PlatformState, INITIAL_CONFIG } from '@angular/platform-server';
import { Type, NgModuleFactory, CompilerFactory, Compiler } from '@angular/core';
import { platformDynamicServer, BEFORE_APP_SERIALIZED, renderModuleFactory } from '@angular/platform-server';
import { ResourceLoader } from '@angular/compiler';

import { REQUEST, ORIGIN_URL } from './tokens';
import { FileLoader } from './file-loader';

import { IEngineOptions } from './interfaces/engine-options';
import { DOCUMENT } from '@angular/platform-browser';

/* @internal */
export class UniversalData {
public static appNode = '';
public static title = '';
public static scripts = '';
public static styles = '';
public static meta = '';
public static links = '';
}

/* @internal */
let appSelector = 'app-root'; // default

/* @internal */
function beforeAppSerialized(
doc: any /* TODO: type definition for Domino - DomAPI Spec (similar to "Document") */
) {

return () => {
const STYLES = [];
const SCRIPTS = [];
const META = [];
const LINKS = [];

for (let i = 0; i < doc.head.children.length; i++) {
const element = doc.head.children[i];
const tagName = element.tagName.toUpperCase();

switch (tagName) {
case 'SCRIPT':
SCRIPTS.push(element.outerHTML);
break;
case 'STYLE':
STYLES.push(element.outerHTML);
break;
case 'LINK':
LINKS.push(element.outerHTML);
break;
case 'META':
META.push(element.outerHTML);
break;
default:
break;
}
}

UniversalData.title = doc.title;
UniversalData.appNode = doc.querySelector(appSelector).outerHTML;
UniversalData.scripts = SCRIPTS.join(' ');
UniversalData.styles = STYLES.join(' ');
UniversalData.meta = META.join(' ');
UniversalData.links = LINKS.join(' ');
};
}

import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/first';

export function ngAspnetCoreEngine(
options: IEngineOptions
): Promise<{ html: string, globals: { styles: string, title: string, meta: string, transferData?: {}, [key: string]: any } }> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

transferData looks like it's unused, is this from before we had TransferState?


if (!options.appSelector) {
throw new Error(`appSelector is required! Pass in " appSelector: '<app-root></app-root>' ", for your root App component.`);
}

// Grab the DOM "selector" from the passed in Template <app-root> for example = "app-root"
appSelector = options.appSelector.substring(1, options.appSelector.indexOf('>'));

const compilerFactory: CompilerFactory = platformDynamicServer().injector.get(CompilerFactory);
const compiler: Compiler = compilerFactory.createCompiler([
{
Expand All @@ -34,148 +95,59 @@ export function ngAspnetCoreEngine(
options.providers = options.providers || [];

const extraProviders = options.providers.concat(
options.providers,
[
{
provide: INITIAL_CONFIG,
useValue: {
document: options.appSelector,
url: options.request.url
}
},
{
...options.providers,
[{
provide: ORIGIN_URL,
useValue: options.request.origin
}, {
provide: REQUEST,
useValue: options.request.data.request
}, {
provide: BEFORE_APP_SERIALIZED,
useFactory: beforeAppSerialized, multi: true, deps: [ DOCUMENT ]
}
]
);

const platform = platformServer(extraProviders);

getFactory(moduleOrFactory, compiler)
.then((factory: NgModuleFactory<{}>) => {

return platform.bootstrapModuleFactory(factory).then((moduleRef: NgModuleRef<{}>) => {

const state: PlatformState = moduleRef.injector.get(PlatformState);
const appRef: ApplicationRef = moduleRef.injector.get(ApplicationRef);

appRef.isStable
.filter((isStable: boolean) => isStable)
.first()
.subscribe(() => {

// Fire the TransferState Cache
const bootstrap = (<{ ngOnBootstrap?: Function }> moduleRef.instance).ngOnBootstrap;
bootstrap && bootstrap();

// The Document itself
const AST_DOCUMENT = state.getDocument();

// Strip out the Angular application
const htmlDoc = state.renderToString();

const APP_HTML = htmlDoc.substring(
htmlDoc.indexOf('<body>') + 6,
htmlDoc.indexOf('</body>')
);

// Strip out Styles / Meta-tags / Title
// const STYLES = [];
const META = [];
const LINKS = [];
let TITLE = '';

let STYLES_STRING = htmlDoc.substring(
htmlDoc.indexOf('<style ng-transition'),
htmlDoc.lastIndexOf('</style>') + 8
);
// STYLES_STRING = STYLES_STRING.replace(/\s/g, '').replace('<styleng-transition', '<style ng-transition');

const HEAD = AST_DOCUMENT.head;

let count = 0;

for (let i = 0; i < HEAD.children.length; i++) {
let element = HEAD.children[i];

if (element.name === 'title') {
TITLE = element.children[0].data;
}

// Broken after 4.0 (worked in rc) - needs investigation
// As other things could be in <style> so we ideally want to get them this way

// if (element.name === 'style') {
// let styleTag = '<style ';
// for (let key in element.attribs) {
// if (key) {
// styleTag += `${key}="${element.attribs[key]}">`;
// }
// }

// styleTag += `${element.children[0].data}</style>`;
// STYLES.push(styleTag);
// }

if (element.name === 'meta') {
count = count + 1;
let metaString = '<meta';
for (let key in element.attribs) {
if (key) {
metaString += ` ${key}="${element.attribs[key]}"`;
}
}
META.push(`${metaString} />\n`);
}

if (element.name === 'link') {
let linkString = '<link';
for (let key in element.attribs) {
if (key) {
linkString += ` ${key}="${element.attribs[key]}"`;
}
}
LINKS.push(`${linkString} />\n`);
}
}

// Return parsed App
resolve({
html: APP_HTML,
globals: {
styles: STYLES_STRING,
title: TITLE,
meta: META.join(' '),
links: LINKS.join(' ')
}
});

moduleRef.destroy();

}, (err) => {
reject(err);
});
.then(factory => {
return renderModuleFactory(factory, {
document: options.appSelector,
url: options.request.url,
extraProviders: extraProviders
});
})
.then(() => {

resolve({
html: UniversalData.appNode,
globals: {
styles: UniversalData.styles,
title: UniversalData.title,
scripts: UniversalData.scripts,
meta: UniversalData.meta,
links: UniversalData.links
}

});
}, (err) => {
reject(err);
});

} catch (ex) {
reject(ex);
}

});
}

/* ********************** Private / Internal ****************** */
}

/* @internal */
const factoryCacheMap = new Map<Type<{}>, NgModuleFactory<{}>>();
function getFactory(
moduleOrFactory: Type<{}> | NgModuleFactory<{}>, compiler: Compiler
): Promise<NgModuleFactory<{}>> {

return new Promise<NgModuleFactory<{}>>((resolve, reject) => {
// If module has been compiled AoT
if (moduleOrFactory instanceof NgModuleFactory) {
Expand Down
1 change: 1 addition & 0 deletions tslint.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"defaultSeverity": "warning",
"rules": {
"comment-format": [true, "check-space"],
"class-name": true,
Expand Down