Skip to content

Commit a992114

Browse files
committed
Unify component discovery code and use webassembly instead of 'client'
1 parent 3d96df5 commit a992114

File tree

11 files changed

+408
-481
lines changed

11 files changed

+408
-481
lines changed

src/Components/Web.JS/dist/Release/blazor.server.js

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Components/Web.JS/dist/Release/blazor.webassembly.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Components/Web.JS/src/Boot.Server.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ import { shouldAutoStart } from './BootCommon';
77
import { RenderQueue } from './Platform/Circuits/RenderQueue';
88
import { ConsoleLogger } from './Platform/Logging/Loggers';
99
import { LogLevel, Logger } from './Platform/Logging/Logger';
10-
import { discoverComponents, CircuitDescriptor } from './Platform/Circuits/CircuitManager';
10+
import { CircuitDescriptor } from './Platform/Circuits/CircuitManager';
1111
import { setEventDispatcher } from './Rendering/RendererEventDispatcher';
1212
import { resolveOptions, CircuitStartOptions } from './Platform/Circuits/CircuitStartOptions';
1313
import { DefaultReconnectionHandler } from './Platform/Circuits/DefaultReconnectionHandler';
1414
import { attachRootComponentToLogicalElement } from './Rendering/Renderer';
15+
import { discoverComponents, ServerComponentDescriptor } from './Services/ComponentDescriptorDiscovery';
1516

1617
let renderingFailed = false;
1718
let started = false;
@@ -29,7 +30,7 @@ async function boot(userOptions?: Partial<CircuitStartOptions>): Promise<void> {
2930
options.reconnectionHandler = options.reconnectionHandler || window['Blazor'].defaultReconnectionHandler;
3031
logger.log(LogLevel.Information, 'Starting up blazor server-side application.');
3132

32-
const components = discoverComponents(document);
33+
const components = discoverComponents(document, 'server') as ServerComponentDescriptor[];
3334
const circuit = new CircuitDescriptor(components);
3435

3536
const initialConnection = await initializeConnection(options, logger, circuit);

src/Components/Web.JS/src/Boot.WebAssembly.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import { WebAssemblyConfigLoader } from './Platform/WebAssemblyConfigLoader';
1111
import { BootConfigResult } from './Platform/BootConfig';
1212
import { Pointer } from './Platform/Platform';
1313
import { WebAssemblyStartOptions } from './Platform/WebAssemblyStartOptions';
14-
import { discoverComponents, WebAssemblyComponentAttacher } from './Platform/WebAssemblyComponentAttacher';
14+
import { WebAssemblyComponentAttacher } from './Platform/WebAssemblyComponentAttacher';
15+
import { discoverComponents, WebAssemblyComponentDescriptor } from './Services/ComponentDescriptorDiscovery';
1516

1617
let started = false;
1718

@@ -76,7 +77,7 @@ async function boot(options?: Partial<WebAssemblyStartOptions>): Promise<void> {
7677

7778
// Leverage the time while we are loading boot.config.json from the network to discover any potentially registered component on
7879
// the document.
79-
const discoveredComponents = discoverComponents(document);
80+
const discoveredComponents = discoverComponents(document, 'webassembly') as WebAssemblyComponentDescriptor[];
8081
const componentAttacher = new WebAssemblyComponentAttacher(discoveredComponents);
8182
window['Blazor']._internal.registeredComponents = {
8283
getRegisteredComponentsCount: () => componentAttacher.getCount(),
Lines changed: 3 additions & 223 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { internalFunctions as navigationManagerFunctions } from '../../Services/NavigationManager';
22
import { toLogicalRootCommentElement, LogicalElement } from '../../Rendering/LogicalElements';
3+
import { ServerComponentDescriptor } from '../../Services/ComponentDescriptorDiscovery';
34

45
export class CircuitDescriptor {
56
public circuitId?: string;
67

7-
public components: ComponentDescriptor[];
8+
public components: ServerComponentDescriptor[];
89

9-
public constructor(components: ComponentDescriptor[]) {
10+
public constructor(components: ServerComponentDescriptor[]) {
1011
this.circuitId = undefined;
1112
this.components = components;
1213
}
@@ -54,224 +55,3 @@ export class CircuitDescriptor {
5455
}
5556
}
5657

57-
interface ComponentMarker {
58-
type: string;
59-
sequence: number;
60-
descriptor: string;
61-
}
62-
63-
export class ComponentDescriptor {
64-
public type: string;
65-
66-
public start: Node;
67-
68-
public end?: Node;
69-
70-
public sequence: number;
71-
72-
public descriptor: string;
73-
74-
public constructor(type: string, start: Node, end: Node | undefined, sequence: number, descriptor: string) {
75-
this.type = type;
76-
this.start = start;
77-
this.end = end;
78-
this.sequence = sequence;
79-
this.descriptor = descriptor;
80-
}
81-
82-
public toRecord(): ComponentMarker {
83-
const result = { type: this.type, sequence: this.sequence, descriptor: this.descriptor };
84-
return result;
85-
}
86-
}
87-
88-
export function discoverComponents(document: Document): ComponentDescriptor[] {
89-
const componentComments = resolveComponentComments(document);
90-
const discoveredComponents: ComponentDescriptor[] = [];
91-
for (let i = 0; i < componentComments.length; i++) {
92-
const componentComment = componentComments[i];
93-
const entry = new ComponentDescriptor(
94-
componentComment.type,
95-
componentComment.start,
96-
componentComment.end,
97-
componentComment.sequence,
98-
componentComment.descriptor,
99-
);
100-
101-
discoveredComponents.push(entry);
102-
}
103-
104-
return discoveredComponents.sort((a, b) => a.sequence - b.sequence);
105-
}
106-
107-
108-
interface ComponentComment {
109-
type: 'server' | 'client';
110-
sequence: number;
111-
descriptor: string;
112-
start: Node;
113-
end?: Node;
114-
prerenderId?: string;
115-
}
116-
117-
function resolveComponentComments(node: Node): ComponentComment[] {
118-
if (!node.hasChildNodes()) {
119-
return [];
120-
}
121-
122-
const result: ComponentComment[] = [];
123-
const childNodeIterator = new ComponentCommentIterator(node.childNodes);
124-
while (childNodeIterator.next() && childNodeIterator.currentElement) {
125-
const componentComment = getComponentComment(childNodeIterator);
126-
if (componentComment) {
127-
result.push(componentComment);
128-
} else {
129-
const childResults = resolveComponentComments(childNodeIterator.currentElement);
130-
for (let j = 0; j < childResults.length; j++) {
131-
const childResult = childResults[j];
132-
result.push(childResult);
133-
}
134-
}
135-
}
136-
137-
return result;
138-
}
139-
140-
const blazorCommentRegularExpression = /\W*Blazor:[^{]*(?<descriptor>.*)$/;
141-
142-
function getComponentComment(commentNodeIterator: ComponentCommentIterator): ComponentComment | undefined {
143-
const candidateStart = commentNodeIterator.currentElement;
144-
145-
if (!candidateStart || candidateStart.nodeType !== Node.COMMENT_NODE) {
146-
return;
147-
}
148-
if (candidateStart.textContent) {
149-
const componentStartComment = new RegExp(blazorCommentRegularExpression);
150-
const definition = componentStartComment.exec(candidateStart.textContent);
151-
const json = definition && definition.groups && definition.groups['descriptor'];
152-
153-
if (json) {
154-
try {
155-
return createServerComponentComment(json, candidateStart, commentNodeIterator);
156-
} catch (error) {
157-
throw new Error(`Found malformed component comment at ${candidateStart.textContent}`);
158-
}
159-
} else {
160-
return;
161-
}
162-
}
163-
}
164-
165-
function createServerComponentComment(json: string, start: Node, iterator: ComponentCommentIterator): ComponentComment | undefined {
166-
const payload = JSON.parse(json) as ComponentComment;
167-
const { type, sequence, descriptor, prerenderId } = payload;
168-
if (type !== 'server' && type !== 'client') {
169-
throw new Error(`Invalid component type '${type}'.`);
170-
}
171-
172-
// We should not encounter this, but in the case we do, simply ignore it.
173-
if (type === 'client') {
174-
return undefined;
175-
}
176-
177-
if (!descriptor) {
178-
throw new Error('descriptor must be defined when using a descriptor.');
179-
}
180-
181-
if (sequence === undefined) {
182-
throw new Error('sequence must be defined when using a descriptor.');
183-
}
184-
185-
if (!Number.isInteger(sequence)) {
186-
throw new Error(`Error parsing the sequence '${sequence}' for component '${json}'`);
187-
}
188-
189-
if (!prerenderId) {
190-
return {
191-
type,
192-
sequence: sequence,
193-
descriptor,
194-
start,
195-
};
196-
} else {
197-
const end = getComponentEndComment(prerenderId, iterator);
198-
if (!end) {
199-
throw new Error(`Could not find an end component comment for '${start}'`);
200-
}
201-
202-
return {
203-
type,
204-
sequence,
205-
descriptor,
206-
start,
207-
prerenderId,
208-
end,
209-
};
210-
}
211-
}
212-
213-
function getComponentEndComment(prerenderedId: string, iterator: ComponentCommentIterator): ChildNode | undefined {
214-
while (iterator.next() && iterator.currentElement) {
215-
const node = iterator.currentElement;
216-
if (node.nodeType !== Node.COMMENT_NODE) {
217-
continue;
218-
}
219-
if (!node.textContent) {
220-
continue;
221-
}
222-
223-
const definition = new RegExp(blazorCommentRegularExpression).exec(node.textContent);
224-
const json = definition && definition[1];
225-
if (!json) {
226-
continue;
227-
}
228-
229-
validateEndComponentPayload(json, prerenderedId);
230-
231-
return node;
232-
}
233-
234-
return undefined;
235-
}
236-
237-
function validateEndComponentPayload(json: string, prerenderedId: string): void {
238-
const payload = JSON.parse(json) as ComponentComment;
239-
if (Object.keys(payload).length !== 1) {
240-
throw new Error(`Invalid end of component comment: '${json}'`);
241-
}
242-
const prerenderedEndId = payload.prerenderId;
243-
if (!prerenderedEndId) {
244-
throw new Error(`End of component comment must have a value for the prerendered property: '${json}'`);
245-
}
246-
if (prerenderedEndId !== prerenderedId) {
247-
throw new Error(`End of component comment prerendered property must match the start comment prerender id: '${prerenderedId}', '${prerenderedEndId}'`);
248-
}
249-
}
250-
251-
class ComponentCommentIterator {
252-
253-
private childNodes: NodeListOf<ChildNode>;
254-
255-
private currentIndex: number;
256-
257-
private length: number;
258-
259-
public currentElement: ChildNode | undefined;
260-
261-
public constructor(childNodes: NodeListOf<ChildNode>) {
262-
this.childNodes = childNodes;
263-
this.currentIndex = -1;
264-
this.length = childNodes.length;
265-
}
266-
267-
public next(): boolean {
268-
this.currentIndex++;
269-
if (this.currentIndex < this.length) {
270-
this.currentElement = this.childNodes[this.currentIndex];
271-
return true;
272-
} else {
273-
this.currentElement = undefined;
274-
return false;
275-
}
276-
}
277-
}

0 commit comments

Comments
 (0)