fix(ivy): set `ng-version` attribute on root component (#27175)
PR Close #27175
This commit is contained in:
parent
01917733a1
commit
bf3beb5959
|
@ -16,16 +16,16 @@ import {ElementRef as viewEngine_ElementRef} from '../linker/element_ref';
|
||||||
import {NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory';
|
import {NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory';
|
||||||
import {RendererFactory2} from '../render/api';
|
import {RendererFactory2} from '../render/api';
|
||||||
import {Type} from '../type';
|
import {Type} from '../type';
|
||||||
|
import {VERSION} from '../version';
|
||||||
import {assertComponentType, assertDefined} from './assert';
|
import {assertComponentType, assertDefined} from './assert';
|
||||||
import {LifecycleHooksFeature, createRootComponent, createRootComponentView, createRootContext} from './component';
|
import {LifecycleHooksFeature, createRootComponent, createRootComponentView, createRootContext} from './component';
|
||||||
import {getComponentDef} from './definition';
|
import {getComponentDef} from './definition';
|
||||||
import {NodeInjector} from './di';
|
import {NodeInjector} from './di';
|
||||||
import {createLViewData, createNodeAtIndex, createTView, createViewNode, elementCreate, locateHostElement, refreshDescendantViews} from './instructions';
|
import {createLViewData, createNodeAtIndex, createTView, createViewNode, elementCreate, locateHostElement, refreshDescendantViews} from './instructions';
|
||||||
import {ComponentDef, RenderFlags} from './interfaces/definition';
|
import {ComponentDef, RenderFlags} from './interfaces/definition';
|
||||||
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node';
|
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType} from './interfaces/node';
|
||||||
import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
|
import {RElement, RendererFactory3, domRendererFactory3, isProceduralRenderer} from './interfaces/renderer';
|
||||||
import {FLAGS, HEADER_OFFSET, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view';
|
import {HEADER_OFFSET, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view';
|
||||||
import {enterView, leaveView} from './state';
|
import {enterView, leaveView} from './state';
|
||||||
import {defaultScheduler, getTNode} from './util';
|
import {defaultScheduler, getTNode} from './util';
|
||||||
import {createElementRef} from './view_engine_compatibility';
|
import {createElementRef} from './view_engine_compatibility';
|
||||||
|
@ -141,6 +141,14 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
|
||||||
const renderer = rendererFactory.createRenderer(hostRNode, this.componentDef);
|
const renderer = rendererFactory.createRenderer(hostRNode, this.componentDef);
|
||||||
const rootViewInjector =
|
const rootViewInjector =
|
||||||
ngModule ? createChainedInjector(injector, ngModule.injector) : injector;
|
ngModule ? createChainedInjector(injector, ngModule.injector) : injector;
|
||||||
|
|
||||||
|
if (rootSelectorOrNode && hostRNode) {
|
||||||
|
ngDevMode && ngDevMode.rendererSetAttribute++;
|
||||||
|
isProceduralRenderer(renderer) ?
|
||||||
|
renderer.setAttribute(hostRNode, 'ng-version', VERSION.full) :
|
||||||
|
hostRNode.setAttribute('ng-version', VERSION.full);
|
||||||
|
}
|
||||||
|
|
||||||
// Create the root view. Uses empty TView and ContentTemplate.
|
// Create the root view. Uses empty TView and ContentTemplate.
|
||||||
const rootView: LViewData = createLViewData(
|
const rootView: LViewData = createLViewData(
|
||||||
renderer, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags, undefined,
|
renderer, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags, undefined,
|
||||||
|
|
|
@ -440,6 +440,9 @@
|
||||||
{
|
{
|
||||||
"name": "UnsubscriptionErrorImpl"
|
"name": "UnsubscriptionErrorImpl"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "VERSION"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "VIEWS"
|
"name": "VIEWS"
|
||||||
},
|
},
|
||||||
|
|
|
@ -926,6 +926,9 @@
|
||||||
{
|
{
|
||||||
"name": "VALID_ELEMENTS"
|
"name": "VALID_ELEMENTS"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "VERSION"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "VIEWS"
|
"name": "VIEWS"
|
||||||
},
|
},
|
||||||
|
|
|
@ -539,32 +539,29 @@ class HiddenModule {
|
||||||
});
|
});
|
||||||
afterEach(() => { expect(called).toBe(true); });
|
afterEach(() => { expect(called).toBe(true); });
|
||||||
|
|
||||||
fixmeIvy('to investigate') &&
|
it('using long form should work', async(() => {
|
||||||
it('using long form should work', async(() => {
|
const platform =
|
||||||
const platform =
|
platformDynamicServer([{provide: INITIAL_CONFIG, useValue: {document: doc}}]);
|
||||||
platformDynamicServer([{provide: INITIAL_CONFIG, useValue: {document: doc}}]);
|
|
||||||
|
|
||||||
platform.bootstrapModule(AsyncServerModule)
|
platform.bootstrapModule(AsyncServerModule)
|
||||||
.then((moduleRef) => {
|
.then((moduleRef) => {
|
||||||
const applicationRef: ApplicationRef = moduleRef.injector.get(ApplicationRef);
|
const applicationRef: ApplicationRef = moduleRef.injector.get(ApplicationRef);
|
||||||
return applicationRef.isStable.pipe(first((isStable: boolean) => isStable))
|
return applicationRef.isStable.pipe(first((isStable: boolean) => isStable))
|
||||||
.toPromise();
|
.toPromise();
|
||||||
})
|
})
|
||||||
.then((b) => {
|
.then((b) => {
|
||||||
expect(platform.injector.get(PlatformState).renderToString())
|
expect(platform.injector.get(PlatformState).renderToString()).toBe(expectedOutput);
|
||||||
.toBe(expectedOutput);
|
platform.destroy();
|
||||||
platform.destroy();
|
|
||||||
called = true;
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
fixmeIvy('to investigate') &&
|
|
||||||
it('using renderModule should work', async(() => {
|
|
||||||
renderModule(AsyncServerModule, {document: doc}).then(output => {
|
|
||||||
expect(output).toBe(expectedOutput);
|
|
||||||
called = true;
|
called = true;
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('using renderModule should work', async(() => {
|
||||||
|
renderModule(AsyncServerModule, {document: doc}).then(output => {
|
||||||
|
expect(output).toBe(expectedOutput);
|
||||||
|
called = true;
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('using renderModuleFactory should work',
|
it('using renderModuleFactory should work',
|
||||||
async(inject([PlatformRef], (defaultPlatform: PlatformRef) => {
|
async(inject([PlatformRef], (defaultPlatform: PlatformRef) => {
|
||||||
|
@ -609,14 +606,13 @@ class HiddenModule {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
fixmeIvy('to investigate') &&
|
it('sets a prefix for the _nghost and _ngcontent attributes', async(() => {
|
||||||
it('sets a prefix for the _nghost and _ngcontent attributes', async(() => {
|
renderModule(ExampleStylesModule, {document: doc}).then(output => {
|
||||||
renderModule(ExampleStylesModule, {document: doc}).then(output => {
|
expect(output).toMatch(
|
||||||
expect(output).toMatch(
|
/<html><head><style ng-transition="example-styles">div\[_ngcontent-sc\d+\] {color: blue; } \[_nghost-sc\d+\] { color: red; }<\/style><\/head><body><app _nghost-sc\d+="" ng-version="0.0.0-PLACEHOLDER"><div _ngcontent-sc\d+="">Works!<\/div><\/app><\/body><\/html>/);
|
||||||
/<html><head><style ng-transition="example-styles">div\[_ngcontent-sc\d+\] {color: blue; } \[_nghost-sc\d+\] { color: red; }<\/style><\/head><body><app _nghost-sc\d+="" ng-version="0.0.0-PLACEHOLDER"><div _ngcontent-sc\d+="">Works!<\/div><\/app><\/body><\/html>/);
|
called = true;
|
||||||
called = true;
|
});
|
||||||
});
|
}));
|
||||||
}));
|
|
||||||
|
|
||||||
fixmeIvy('to investigate') &&
|
fixmeIvy('to investigate') &&
|
||||||
it('should handle false values on attributes', async(() => {
|
it('should handle false values on attributes', async(() => {
|
||||||
|
@ -662,29 +658,27 @@ class HiddenModule {
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
fixmeIvy('to investigate') &&
|
it('should call render hook', async(() => {
|
||||||
it('should call render hook', async(() => {
|
renderModule(RenderHookModule, {document: doc}).then(output => {
|
||||||
renderModule(RenderHookModule, {document: doc}).then(output => {
|
// title should be added by the render hook.
|
||||||
// title should be added by the render hook.
|
expect(output).toBe(
|
||||||
expect(output).toBe(
|
'<html><head><title>RenderHook</title></head><body>' +
|
||||||
'<html><head><title>RenderHook</title></head><body>' +
|
'<app ng-version="0.0.0-PLACEHOLDER">Works!</app></body></html>');
|
||||||
'<app ng-version="0.0.0-PLACEHOLDER">Works!</app></body></html>');
|
called = true;
|
||||||
called = true;
|
});
|
||||||
});
|
}));
|
||||||
}));
|
|
||||||
|
|
||||||
fixmeIvy('to investigate') &&
|
it('should call multiple render hooks', async(() => {
|
||||||
it('should call multiple render hooks', async(() => {
|
const consoleSpy = spyOn(console, 'warn');
|
||||||
const consoleSpy = spyOn(console, 'warn');
|
renderModule(MultiRenderHookModule, {document: doc}).then(output => {
|
||||||
renderModule(MultiRenderHookModule, {document: doc}).then(output => {
|
// title should be added by the render hook.
|
||||||
// title should be added by the render hook.
|
expect(output).toBe(
|
||||||
expect(output).toBe(
|
'<html><head><title>RenderHook</title><meta name="description"></head>' +
|
||||||
'<html><head><title>RenderHook</title><meta name="description"></head>' +
|
'<body><app ng-version="0.0.0-PLACEHOLDER">Works!</app></body></html>');
|
||||||
'<body><app ng-version="0.0.0-PLACEHOLDER">Works!</app></body></html>');
|
expect(consoleSpy).toHaveBeenCalled();
|
||||||
expect(consoleSpy).toHaveBeenCalled();
|
called = true;
|
||||||
called = true;
|
});
|
||||||
});
|
}));
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('http', () => {
|
describe('http', () => {
|
||||||
|
@ -856,13 +850,12 @@ class HiddenModule {
|
||||||
beforeEach(() => { called = false; });
|
beforeEach(() => { called = false; });
|
||||||
afterEach(() => { expect(called).toBe(true); });
|
afterEach(() => { expect(called).toBe(true); });
|
||||||
|
|
||||||
fixmeIvy('to investigate') &&
|
it('adds transfer script tag when using renderModule', async(() => {
|
||||||
it('adds transfer script tag when using renderModule', async(() => {
|
renderModule(TransferStoreModule, {document: '<app></app>'}).then(output => {
|
||||||
renderModule(TransferStoreModule, {document: '<app></app>'}).then(output => {
|
expect(output).toBe(defaultExpectedOutput);
|
||||||
expect(output).toBe(defaultExpectedOutput);
|
called = true;
|
||||||
called = true;
|
});
|
||||||
});
|
}));
|
||||||
}));
|
|
||||||
|
|
||||||
it('adds transfer script tag when using renderModuleFactory',
|
it('adds transfer script tag when using renderModuleFactory',
|
||||||
async(inject([PlatformRef], (defaultPlatform: PlatformRef) => {
|
async(inject([PlatformRef], (defaultPlatform: PlatformRef) => {
|
||||||
|
@ -876,19 +869,18 @@ class HiddenModule {
|
||||||
});
|
});
|
||||||
})));
|
})));
|
||||||
|
|
||||||
fixmeIvy('to investigate') &&
|
it('cannot break out of <script> tag in serialized output', async(() => {
|
||||||
it('cannot break out of <script> tag in serialized output', async(() => {
|
renderModule(EscapedTransferStoreModule, {
|
||||||
renderModule(EscapedTransferStoreModule, {
|
document: '<esc-app></esc-app>'
|
||||||
document: '<esc-app></esc-app>'
|
}).then(output => {
|
||||||
}).then(output => {
|
expect(output).toBe(
|
||||||
expect(output).toBe(
|
'<html><head></head><body><esc-app ng-version="0.0.0-PLACEHOLDER">Works!</esc-app>' +
|
||||||
'<html><head></head><body><esc-app ng-version="0.0.0-PLACEHOLDER">Works!</esc-app>' +
|
'<script id="transfer-state" type="application/json">' +
|
||||||
'<script id="transfer-state" type="application/json">' +
|
'{&q;testString&q;:&q;&l;/script&g;&l;script&g;' +
|
||||||
'{&q;testString&q;:&q;&l;/script&g;&l;script&g;' +
|
'alert(&s;Hello&a;&s; + \\&q;World\\&q;);&q;}</script></body></html>');
|
||||||
'alert(&s;Hello&a;&s; + \\&q;World\\&q;);&q;}</script></body></html>');
|
called = true;
|
||||||
called = true;
|
});
|
||||||
});
|
}));
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
Loading…
Reference in New Issue