improve control flow transformer robustness and remove debug logs
This commit is contained in:
parent
990aef92ef
commit
c3dda73d3a
|
|
@ -56,10 +56,20 @@ export class ControlFlowTransformer {
|
||||||
closeParenIndex--;
|
closeParenIndex--;
|
||||||
|
|
||||||
const openBraceIndex = content.indexOf('{', closeParenIndex);
|
const openBraceIndex = content.indexOf('{', closeParenIndex);
|
||||||
if (openBraceIndex === -1) return null;
|
if (openBraceIndex === -1) {
|
||||||
|
// Handle case where there's no opening brace - create a simple block
|
||||||
|
return {
|
||||||
|
match: content.substring(ifIndex, closeParenIndex + 1) + '{ }',
|
||||||
|
startIndex: ifIndex,
|
||||||
|
endIndex: closeParenIndex + 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let endIndex = this.findIfBlockEnd(content, openBraceIndex);
|
let endIndex = this.findIfBlockEnd(content, openBraceIndex);
|
||||||
if (endIndex === -1) return null;
|
if (endIndex === -1) {
|
||||||
|
// For incomplete blocks, try to process what we have
|
||||||
|
endIndex = content.length;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
match: content.substring(ifIndex, endIndex),
|
match: content.substring(ifIndex, endIndex),
|
||||||
|
|
@ -79,7 +89,21 @@ export class ControlFlowTransformer {
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (braceCount !== 0) return -1;
|
// If we couldn't find matching closing brace, try to handle incomplete blocks
|
||||||
|
if (braceCount !== 0) {
|
||||||
|
// For incomplete blocks, find the end of the current line or next @if/@for statement
|
||||||
|
const remainingContent = content.substring(startBraceIndex);
|
||||||
|
const nextIfIndex = remainingContent.indexOf('@if', 1);
|
||||||
|
const nextForIndex = remainingContent.indexOf('@for', 1);
|
||||||
|
const lineEndIndex = remainingContent.indexOf('\n');
|
||||||
|
|
||||||
|
let endIndex = content.length;
|
||||||
|
if (nextIfIndex !== -1) endIndex = Math.min(endIndex, startBraceIndex + nextIfIndex);
|
||||||
|
if (nextForIndex !== -1) endIndex = Math.min(endIndex, startBraceIndex + nextForIndex);
|
||||||
|
if (lineEndIndex !== -1) endIndex = Math.min(endIndex, startBraceIndex + lineEndIndex);
|
||||||
|
|
||||||
|
return endIndex;
|
||||||
|
}
|
||||||
|
|
||||||
while (index < content.length) {
|
while (index < content.length) {
|
||||||
const remaining = content.substring(index);
|
const remaining = content.substring(index);
|
||||||
|
|
@ -165,7 +189,21 @@ export class ControlFlowTransformer {
|
||||||
if (forIndex === -1) return null;
|
if (forIndex === -1) return null;
|
||||||
|
|
||||||
const openParenIndex = content.indexOf('(', forIndex);
|
const openParenIndex = content.indexOf('(', forIndex);
|
||||||
const closeParenIndex = content.indexOf(')', openParenIndex);
|
if (openParenIndex === -1) return null;
|
||||||
|
|
||||||
|
// Properly count nested parentheses
|
||||||
|
let parenCount = 1;
|
||||||
|
let closeParenIndex = openParenIndex + 1;
|
||||||
|
while (closeParenIndex < content.length && parenCount > 0) {
|
||||||
|
const char = content[closeParenIndex];
|
||||||
|
if (char === '(') parenCount++;
|
||||||
|
else if (char === ')') parenCount--;
|
||||||
|
closeParenIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parenCount !== 0) return null;
|
||||||
|
closeParenIndex--; // Move back to the closing paren
|
||||||
|
|
||||||
const openBraceIndex = content.indexOf('{', closeParenIndex);
|
const openBraceIndex = content.indexOf('{', closeParenIndex);
|
||||||
|
|
||||||
if (openBraceIndex === -1) return null;
|
if (openBraceIndex === -1) return null;
|
||||||
|
|
@ -194,7 +232,21 @@ export class ControlFlowTransformer {
|
||||||
if (startIndex === -1) return null;
|
if (startIndex === -1) return null;
|
||||||
|
|
||||||
const openParenIndex = match.indexOf('(', startIndex);
|
const openParenIndex = match.indexOf('(', startIndex);
|
||||||
const closeParenIndex = match.indexOf(')', openParenIndex);
|
if (openParenIndex === -1) return null;
|
||||||
|
|
||||||
|
// Properly count nested parentheses
|
||||||
|
let parenCount = 1;
|
||||||
|
let closeParenIndex = openParenIndex + 1;
|
||||||
|
while (closeParenIndex < match.length && parenCount > 0) {
|
||||||
|
const char = match[closeParenIndex];
|
||||||
|
if (char === '(') parenCount++;
|
||||||
|
else if (char === ')') parenCount--;
|
||||||
|
closeParenIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parenCount !== 0) return null;
|
||||||
|
closeParenIndex--; // Move back to the closing paren
|
||||||
|
|
||||||
const openBraceIndex = match.indexOf('{', closeParenIndex);
|
const openBraceIndex = match.indexOf('{', closeParenIndex);
|
||||||
|
|
||||||
if (openBraceIndex === -1) return null;
|
if (openBraceIndex === -1) return null;
|
||||||
|
|
@ -219,7 +271,8 @@ export class ControlFlowTransformer {
|
||||||
const forPart = parts[0].trim();
|
const forPart = parts[0].trim();
|
||||||
const trackPart = parts[1]?.trim();
|
const trackPart = parts[1]?.trim();
|
||||||
|
|
||||||
const forMatch = forPart.match(/^\s*([^\s]+)\s+of\s+([^\s]+)\s*$/);
|
// Match: variable of iterable (iterable can contain parentheses, dots, etc.)
|
||||||
|
const forMatch = forPart.match(/^\s*(\w+)\s+of\s+(.+)\s*$/);
|
||||||
if (!forMatch) return null;
|
if (!forMatch) return null;
|
||||||
|
|
||||||
const variable = forMatch[1].trim();
|
const variable = forMatch[1].trim();
|
||||||
|
|
@ -248,7 +301,10 @@ export class ControlFlowTransformer {
|
||||||
ngForExpression += `; trackBy: ${forBlock.trackBy}`;
|
ngForExpression += `; trackBy: ${forBlock.trackBy}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<ng-container *ngFor="${ngForExpression}">${forBlock.content}</ng-container>`;
|
// Recursively transform nested @if and @for blocks in content
|
||||||
|
const transformedContent = this.transform(forBlock.content);
|
||||||
|
|
||||||
|
return `<ng-container *ngFor="${ngForExpression}">${transformedContent}</ng-container>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseBlocks(match: string): ControlFlowBlock[] {
|
private parseBlocks(match: string): ControlFlowBlock[] {
|
||||||
|
|
@ -273,6 +329,12 @@ export class ControlFlowTransformer {
|
||||||
const { condition, aliasVariable } = this.parseConditionWithAlias(conditionStr.trim());
|
const { condition, aliasVariable } = this.parseConditionWithAlias(conditionStr.trim());
|
||||||
|
|
||||||
const openBraceIndex = match.indexOf('{', closeParenIndex);
|
const openBraceIndex = match.indexOf('{', closeParenIndex);
|
||||||
|
if (openBraceIndex === -1) {
|
||||||
|
// Incomplete @if block - no opening brace found
|
||||||
|
blocks.push({ condition, content: '', aliasVariable });
|
||||||
|
return blocks;
|
||||||
|
}
|
||||||
|
|
||||||
let braceCount = 1;
|
let braceCount = 1;
|
||||||
let closeBraceIndex = openBraceIndex + 1;
|
let closeBraceIndex = openBraceIndex + 1;
|
||||||
|
|
||||||
|
|
@ -282,11 +344,22 @@ export class ControlFlowTransformer {
|
||||||
else if (char === '}') braceCount--;
|
else if (char === '}') braceCount--;
|
||||||
closeBraceIndex++;
|
closeBraceIndex++;
|
||||||
}
|
}
|
||||||
closeBraceIndex--;
|
|
||||||
|
|
||||||
const content = match.substring(openBraceIndex + 1, closeBraceIndex);
|
let content = '';
|
||||||
blocks.push({ condition, content, aliasVariable });
|
if (braceCount === 0) {
|
||||||
|
// Complete block found
|
||||||
|
closeBraceIndex--;
|
||||||
|
content = match.substring(openBraceIndex + 1, closeBraceIndex);
|
||||||
index = closeBraceIndex + 1;
|
index = closeBraceIndex + 1;
|
||||||
|
} else {
|
||||||
|
// Incomplete block - take everything after opening brace
|
||||||
|
content = match.substring(openBraceIndex + 1);
|
||||||
|
index = match.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim leading whitespace from content
|
||||||
|
content = content.trimStart();
|
||||||
|
blocks.push({ condition, content, aliasVariable });
|
||||||
}
|
}
|
||||||
|
|
||||||
while (index < match.length) {
|
while (index < match.length) {
|
||||||
|
|
@ -311,6 +384,13 @@ export class ControlFlowTransformer {
|
||||||
const { condition, aliasVariable } = this.parseConditionWithAlias(conditionStr.trim());
|
const { condition, aliasVariable } = this.parseConditionWithAlias(conditionStr.trim());
|
||||||
|
|
||||||
const openBraceIndex = match.indexOf('{', closeParenIndex);
|
const openBraceIndex = match.indexOf('{', closeParenIndex);
|
||||||
|
if (openBraceIndex === -1) {
|
||||||
|
// Incomplete @else if block - no opening brace found
|
||||||
|
blocks.push({ condition, content: '', aliasVariable });
|
||||||
|
index = match.length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
let braceCount = 1;
|
let braceCount = 1;
|
||||||
let closeBraceIndex = openBraceIndex + 1;
|
let closeBraceIndex = openBraceIndex + 1;
|
||||||
|
|
||||||
|
|
@ -320,11 +400,22 @@ export class ControlFlowTransformer {
|
||||||
else if (char === '}') braceCount--;
|
else if (char === '}') braceCount--;
|
||||||
closeBraceIndex++;
|
closeBraceIndex++;
|
||||||
}
|
}
|
||||||
closeBraceIndex--;
|
|
||||||
|
|
||||||
const content = match.substring(openBraceIndex + 1, closeBraceIndex);
|
let content = '';
|
||||||
blocks.push({ condition, content, aliasVariable });
|
if (braceCount === 0) {
|
||||||
|
// Complete block found
|
||||||
|
closeBraceIndex--;
|
||||||
|
content = match.substring(openBraceIndex + 1, closeBraceIndex);
|
||||||
index = closeBraceIndex + 1;
|
index = closeBraceIndex + 1;
|
||||||
|
} else {
|
||||||
|
// Incomplete block - take everything after opening brace
|
||||||
|
content = match.substring(openBraceIndex + 1);
|
||||||
|
index = match.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim leading whitespace from content
|
||||||
|
content = content.trimStart();
|
||||||
|
blocks.push({ condition, content, aliasVariable });
|
||||||
} else if (elseMatch) {
|
} else if (elseMatch) {
|
||||||
const openBraceIndex = index + elseMatch[0].length - 1;
|
const openBraceIndex = index + elseMatch[0].length - 1;
|
||||||
let braceCount = 1;
|
let braceCount = 1;
|
||||||
|
|
@ -336,11 +427,22 @@ export class ControlFlowTransformer {
|
||||||
else if (char === '}') braceCount--;
|
else if (char === '}') braceCount--;
|
||||||
closeBraceIndex++;
|
closeBraceIndex++;
|
||||||
}
|
}
|
||||||
closeBraceIndex--;
|
|
||||||
|
|
||||||
const content = match.substring(openBraceIndex + 1, closeBraceIndex);
|
let content = '';
|
||||||
blocks.push({ condition: null, content });
|
if (braceCount === 0) {
|
||||||
|
// Complete block found
|
||||||
|
closeBraceIndex--;
|
||||||
|
content = match.substring(openBraceIndex + 1, closeBraceIndex);
|
||||||
index = closeBraceIndex + 1;
|
index = closeBraceIndex + 1;
|
||||||
|
} else {
|
||||||
|
// Incomplete block - take everything after opening brace
|
||||||
|
content = match.substring(openBraceIndex + 1);
|
||||||
|
index = match.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim leading whitespace from content
|
||||||
|
content = content.trimStart();
|
||||||
|
blocks.push({ condition: null, content });
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -368,10 +470,13 @@ export class ControlFlowTransformer {
|
||||||
const block = blocks[i];
|
const block = blocks[i];
|
||||||
const condition = this.buildCondition(block.condition, negated);
|
const condition = this.buildCondition(block.condition, negated);
|
||||||
|
|
||||||
|
// Recursively transform nested @if and @for blocks in content
|
||||||
|
const transformedContent = this.transform(block.content);
|
||||||
|
|
||||||
if (block.aliasVariable) {
|
if (block.aliasVariable) {
|
||||||
result += `<ng-container *ngIf="${condition}; let ${block.aliasVariable}">${block.content}</ng-container>`;
|
result += `<ng-container *ngIf="${condition}; let ${block.aliasVariable}">${transformedContent}</ng-container>`;
|
||||||
} else {
|
} else {
|
||||||
result += `<ng-container *ngIf="${condition}">${block.content}</ng-container>`;
|
result += `<ng-container *ngIf="${condition}">${transformedContent}</ng-container>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < blocks.length - 1) {
|
if (i < blocks.length - 1) {
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,7 @@ class Server extends BaseBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private proxyRequest(targetUrl: string, req: http.IncomingMessage, res: http.ServerResponse): void {
|
private proxyRequest(targetUrl: string, req: http.IncomingMessage, res: http.ServerResponse): void {
|
||||||
|
console.log(`[Proxy] ${req.method} ${req.url} -> ${targetUrl}`);
|
||||||
const parsedUrl = new URL(targetUrl);
|
const parsedUrl = new URL(targetUrl);
|
||||||
const protocol = parsedUrl.protocol === 'https:' ? https : http;
|
const protocol = parsedUrl.protocol === 'https:' ? https : http;
|
||||||
|
|
||||||
|
|
@ -173,13 +174,14 @@ class Server extends BaseBuilder {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
(proxyRes) => {
|
(proxyRes) => {
|
||||||
|
console.log(`[Proxy] Response: ${proxyRes.statusCode} for ${req.url}`);
|
||||||
res.writeHead(proxyRes.statusCode || 500, proxyRes.headers);
|
res.writeHead(proxyRes.statusCode || 500, proxyRes.headers);
|
||||||
proxyRes.pipe(res);
|
proxyRes.pipe(res);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
proxyReq.on('error', (err) => {
|
proxyReq.on('error', (err) => {
|
||||||
console.error('Proxy error:', err.message);
|
console.error(`[Proxy] Error for ${req.url}:`, err.message);
|
||||||
res.writeHead(502);
|
res.writeHead(502);
|
||||||
res.end('Bad Gateway');
|
res.end('Bad Gateway');
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -61,27 +61,11 @@ export class Injector {
|
||||||
}
|
}
|
||||||
|
|
||||||
public createInstanceWithProviders<T>(classType: Type<T>, providers: Provider[]): T {
|
public createInstanceWithProviders<T>(classType: Type<T>, providers: Provider[]): T {
|
||||||
console.log('[DI] createInstanceWithProviders START', {
|
|
||||||
classType,
|
|
||||||
typeofClassType: typeof classType,
|
|
||||||
isFunction: typeof classType === 'function',
|
|
||||||
hasName: classType?.name,
|
|
||||||
originalName: (classType as any)?.__quarc_original_name__,
|
|
||||||
providers: providers.map(p => ({
|
|
||||||
provide: typeof p.provide === 'string' ? p.provide : p.provide?.name,
|
|
||||||
type: 'useValue' in p ? 'useValue' : 'useClass' in p ? 'useClass' : 'useFactory' in p ? 'useFactory' : 'useExisting'
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!classType) {
|
if (!classType) {
|
||||||
throw new Error(`[DI] createInstanceWithProviders called with undefined classType`);
|
throw new Error(`[DI] createInstanceWithProviders called with undefined classType`);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log({
|
|
||||||
className: (classType as any).__quarc_original_name__ || classType.name,
|
|
||||||
classType,
|
|
||||||
});
|
|
||||||
const dependencies = this.resolveDependenciesWithProviders(classType, providers);
|
const dependencies = this.resolveDependenciesWithProviders(classType, providers);
|
||||||
/** /
|
/** /
|
||||||
console.log({
|
console.log({
|
||||||
|
|
@ -127,11 +111,6 @@ export class Injector {
|
||||||
return `${metadata.selector} (class)`;
|
return `${metadata.selector} (class)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log({
|
|
||||||
classType,
|
|
||||||
metadata,
|
|
||||||
});
|
|
||||||
|
|
||||||
return 'Unknown class';
|
return 'Unknown class';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -187,44 +166,27 @@ export class Injector {
|
||||||
}
|
}
|
||||||
|
|
||||||
private resolveDependency(token: any, providers: Provider[]): any {
|
private resolveDependency(token: any, providers: Provider[]): any {
|
||||||
console.log('[DI] resolveDependency', {
|
|
||||||
token,
|
|
||||||
typeofToken: typeof token,
|
|
||||||
tokenName: typeof token === 'string' ? token : (token as any).__quarc_original_name__ || token?.name,
|
|
||||||
isFunction: typeof token === 'function',
|
|
||||||
});
|
|
||||||
|
|
||||||
const tokenName = typeof token === 'string' ? token : (token as any).__quarc_original_name__ || token.name;
|
const tokenName = typeof token === 'string' ? token : (token as any).__quarc_original_name__ || token.name;
|
||||||
|
|
||||||
const provider = this.findProvider(token, providers);
|
const provider = this.findProvider(token, providers);
|
||||||
if (provider) {
|
if (provider) {
|
||||||
console.log('[DI] Found provider for token', tokenName);
|
|
||||||
return this.resolveProviderValue(provider, providers);
|
return this.resolveProviderValue(provider, providers);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.sharedInstances[tokenName]) {
|
if (this.sharedInstances[tokenName]) {
|
||||||
console.log('[DI] Found in sharedInstances', tokenName);
|
|
||||||
return this.sharedInstances[tokenName];
|
return this.sharedInstances[tokenName];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.instanceCache[tokenName]) {
|
if (this.instanceCache[tokenName]) {
|
||||||
console.log('[DI] Found in instanceCache', tokenName);
|
|
||||||
return this.instanceCache[tokenName];
|
return this.instanceCache[tokenName];
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[DI] Creating new instance for token', tokenName);
|
|
||||||
return this.createInstanceWithProviders(token, providers);
|
return this.createInstanceWithProviders(token, providers);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getConstructorParameterTypes(classType: Type<any>): any[] {
|
private getConstructorParameterTypes(classType: Type<any>): any[] {
|
||||||
const className = classType?.name || 'Unknown';
|
const className = classType?.name || 'Unknown';
|
||||||
|
|
||||||
console.log({
|
|
||||||
className,
|
|
||||||
classType,
|
|
||||||
diParams: (classType as any).__di_params__,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!classType) {
|
if (!classType) {
|
||||||
throw new Error(`[DI] Cannot resolve dependencies: classType is undefined`);
|
throw new Error(`[DI] Cannot resolve dependencies: classType is undefined`);
|
||||||
}
|
}
|
||||||
|
|
@ -256,12 +218,10 @@ export class Injector {
|
||||||
public register<T>(classType: Type<T>, instance: T | Type<T>): void {
|
public register<T>(classType: Type<T>, instance: T | Type<T>): void {
|
||||||
const key = (classType as any).__quarc_original_name__ || classType.name;
|
const key = (classType as any).__quarc_original_name__ || classType.name;
|
||||||
this.instanceCache[key] = instance;
|
this.instanceCache[key] = instance;
|
||||||
console.log('injector register', classType, key, instance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public registerShared<T>(classType: Type<T>, instance: T | Type<T>): void {
|
public registerShared<T>(classType: Type<T>, instance: T | Type<T>): void {
|
||||||
const key = (classType as any).__quarc_original_name__ || classType.name;
|
const key = (classType as any).__quarc_original_name__ || classType.name;
|
||||||
console.log('injector registerShared', classType, key, instance);
|
|
||||||
this.sharedInstances[key] = instance;
|
this.sharedInstances[key] = instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -235,7 +235,6 @@ export class TemplateFragment {
|
||||||
const isForIn = !!forInMatch;
|
const isForIn = !!forInMatch;
|
||||||
|
|
||||||
if (!match) {
|
if (!match) {
|
||||||
console.warn('Invalid ngFor expression:', ngForExpression);
|
|
||||||
parent.insertBefore(endMarker, ngContainer);
|
parent.insertBefore(endMarker, ngContainer);
|
||||||
ngContainer.remove();
|
ngContainer.remove();
|
||||||
return;
|
return;
|
||||||
|
|
@ -376,7 +375,6 @@ export class TemplateFragment {
|
||||||
*/
|
*/
|
||||||
rerenderFragment(markerIndex: number): void {
|
rerenderFragment(markerIndex: number): void {
|
||||||
if (markerIndex < 0 || markerIndex >= this.ngContainerMarkers.length) {
|
if (markerIndex < 0 || markerIndex >= this.ngContainerMarkers.length) {
|
||||||
console.warn('Invalid marker index:', markerIndex);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ export class WebComponentFactory {
|
||||||
|
|
||||||
const componentMeta = componentType._quarcComponent?.[0];
|
const componentMeta = componentType._quarcComponent?.[0];
|
||||||
if (!componentMeta) {
|
if (!componentMeta) {
|
||||||
console.warn(`Component ${componentType.name} has no _quarcComponent metadata`);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,7 +68,6 @@ export class WebComponentFactory {
|
||||||
this.componentTypes.set(tagName, componentType);
|
this.componentTypes.set(tagName, componentType);
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`Failed to register component ${tagName}:`, error);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -108,7 +106,6 @@ export class WebComponentFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log({localProviders});
|
|
||||||
return injector.createInstanceWithProviders<IComponent>(componentType, localProviders);
|
return injector.createInstanceWithProviders<IComponent>(componentType, localProviders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,11 +35,8 @@ async function tryLoadExternalScripts(urls: string | string[]): Promise<void> {
|
||||||
await loadExternalScript(url);
|
await loadExternalScript(url);
|
||||||
return;
|
return;
|
||||||
} catch {
|
} catch {
|
||||||
console.warn(`[External] Could not load from: ${url}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.info("[External] No external scripts loaded - app continues without enhancements");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function bootstrapApplication(
|
export async function bootstrapApplication(
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ export class RouterLink implements IDirective {
|
||||||
public _nativeElement: HTMLElement,
|
public _nativeElement: HTMLElement,
|
||||||
private activatedRoute?: ActivatedRoute,
|
private activatedRoute?: ActivatedRoute,
|
||||||
) {
|
) {
|
||||||
console.log({ routerLink: this.routerLink() });
|
|
||||||
this._nativeElement.addEventListener('click', (event) => {
|
this._nativeElement.addEventListener('click', (event) => {
|
||||||
this.onClick(event);
|
this.onClick(event);
|
||||||
});
|
});
|
||||||
|
|
@ -44,7 +43,6 @@ export class RouterLink implements IDirective {
|
||||||
|
|
||||||
this.router.navigate(commands, extras).then(success => {
|
this.router.navigate(commands, extras).then(success => {
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error('RouterLink CLICK - Navigation failed:', error);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -261,6 +261,167 @@ test('ControlFlowTransformer: @if @else if oba z aliasem', () => {
|
||||||
result.includes('*ngIf="!(primary()) && !(secondary())"');
|
result.includes('*ngIf="!(primary()) && !(secondary())"');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Test 25: ControlFlowTransformer - niekompletny @if (bez zamknięcia)
|
||||||
|
test('ControlFlowTransformer: niekompletny @if', () => {
|
||||||
|
const transformer = new ControlFlowTransformer();
|
||||||
|
const input = '@if (range.name) { content';
|
||||||
|
const result = transformer.transform(input);
|
||||||
|
return result.includes('<ng-container *ngIf="range.name">content</ng-container>');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 26: ControlFlowTransformer - niekompletny @if na końcu
|
||||||
|
test('ControlFlowTransformer: niekompletny @if na końcu', () => {
|
||||||
|
const transformer = new ControlFlowTransformer();
|
||||||
|
const input = '} @if (prepared.sensor.loading) { ';
|
||||||
|
const result = transformer.transform(input);
|
||||||
|
return result.includes('<ng-container *ngIf="prepared.sensor.loading"></ng-container>');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 27: ControlFlowTransformer - niekompletny @if bez nawiasu klamrowego
|
||||||
|
test('ControlFlowTransformer: niekompletny @if bez nawiasu klamrowego', () => {
|
||||||
|
const transformer = new ControlFlowTransformer();
|
||||||
|
const input = '@if (condition) ';
|
||||||
|
const result = transformer.transform(input);
|
||||||
|
return result.includes('<ng-container *ngIf="condition"></ng-container>');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 28: ControlFlowTransformer - zagnieżdżony @if wewnątrz @for
|
||||||
|
test('ControlFlowTransformer: zagnieżdżony @if wewnątrz @for', () => {
|
||||||
|
const transformer = new ControlFlowTransformer();
|
||||||
|
const input = `@for (item of items) {
|
||||||
|
@if (item.active) {
|
||||||
|
<span>{{ item.name }}</span>
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
const result = transformer.transform(input);
|
||||||
|
return result.includes('<ng-container *ngFor="let item of items">') &&
|
||||||
|
result.includes('<ng-container *ngIf="item.active">') &&
|
||||||
|
!result.includes('@if') &&
|
||||||
|
!result.includes('@for');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 29: ControlFlowTransformer - zagnieżdżony @for wewnątrz @if
|
||||||
|
test('ControlFlowTransformer: zagnieżdżony @for wewnątrz @if', () => {
|
||||||
|
const transformer = new ControlFlowTransformer();
|
||||||
|
const input = `@if (hasItems) {
|
||||||
|
@for (item of items; track item.id) {
|
||||||
|
<div>{{ item }}</div>
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
const result = transformer.transform(input);
|
||||||
|
return result.includes('<ng-container *ngIf="hasItems">') &&
|
||||||
|
result.includes('<ng-container *ngFor="let item of items; trackBy: item.id">') &&
|
||||||
|
!result.includes('@if') &&
|
||||||
|
!result.includes('@for');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 30: ControlFlowTransformer - wielokrotnie zagnieżdżone @if wewnątrz @for
|
||||||
|
test('ControlFlowTransformer: wielokrotnie zagnieżdżone @if wewnątrz @for', () => {
|
||||||
|
const transformer = new ControlFlowTransformer();
|
||||||
|
const input = `@for (range of ranges; track $index) {
|
||||||
|
<div>
|
||||||
|
@if (range.name) {
|
||||||
|
<span>{{ range.name }}</span>
|
||||||
|
}
|
||||||
|
<span>{{ range.min }}</span>
|
||||||
|
</div>
|
||||||
|
}`;
|
||||||
|
const result = transformer.transform(input);
|
||||||
|
return result.includes('<ng-container *ngFor="let range of ranges; trackBy: $index">') &&
|
||||||
|
result.includes('<ng-container *ngIf="range.name">') &&
|
||||||
|
!result.includes('@if') &&
|
||||||
|
!result.includes('@for');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 31: ControlFlowTransformer - kompleksowy przypadek z user template
|
||||||
|
test('ControlFlowTransformer: kompleksowy przypadek użytkownika', () => {
|
||||||
|
const transformer = new ControlFlowTransformer();
|
||||||
|
const input = `@if (prepared.sensor.ranges) {
|
||||||
|
<span>
|
||||||
|
@for (range of prepared.sensor.ranges; track $index) {
|
||||||
|
<div>
|
||||||
|
@if (range.name) {
|
||||||
|
<span>{{ range.name }}</span>
|
||||||
|
}
|
||||||
|
<span>{{ range.min }}</span>
|
||||||
|
<span>{{ range.max }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
}`;
|
||||||
|
const result = transformer.transform(input);
|
||||||
|
return result.includes('<ng-container *ngIf="prepared.sensor.ranges">') &&
|
||||||
|
result.includes('<ng-container *ngFor="let range of prepared.sensor.ranges; trackBy: $index">') &&
|
||||||
|
result.includes('<ng-container *ngIf="range.name">') &&
|
||||||
|
!result.includes('@if') &&
|
||||||
|
!result.includes('@for');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 32: ControlFlowTransformer - głęboko zagnieżdżone @if/@for/@if
|
||||||
|
test('ControlFlowTransformer: głęboko zagnieżdżone @if/@for/@if', () => {
|
||||||
|
const transformer = new ControlFlowTransformer();
|
||||||
|
const input = `@if (level1) {
|
||||||
|
@for (item of items; track item.id) {
|
||||||
|
@if (item.visible) {
|
||||||
|
<div>{{ item.name }}</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
const result = transformer.transform(input);
|
||||||
|
return result.includes('<ng-container *ngIf="level1">') &&
|
||||||
|
result.includes('<ng-container *ngFor="let item of items; trackBy: item.id">') &&
|
||||||
|
result.includes('<ng-container *ngIf="item.visible">') &&
|
||||||
|
!result.includes('@if') &&
|
||||||
|
!result.includes('@for');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 33: ControlFlowTransformer - @for z funkcją w iterable
|
||||||
|
test('ControlFlowTransformer: @for z funkcją w iterable', () => {
|
||||||
|
const transformer = new ControlFlowTransformer();
|
||||||
|
const input = '@for (item of getItems(); track item.id) { <div>{{ item }}</div> }';
|
||||||
|
const result = transformer.transform(input);
|
||||||
|
return result.includes('<ng-container *ngFor="let item of getItems(); trackBy: item.id">') &&
|
||||||
|
!result.includes('@for');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 34: ControlFlowTransformer - @for z zagnieżdżonymi nawiasami w funkcji
|
||||||
|
test('ControlFlowTransformer: @for z zagnieżdżonymi nawiasami w funkcji', () => {
|
||||||
|
const transformer = new ControlFlowTransformer();
|
||||||
|
const input = '@for (prepared of preparedSensors(); track prepared.sensor.id) { <div>{{ prepared.sensor.name }}</div> }';
|
||||||
|
const result = transformer.transform(input);
|
||||||
|
return result.includes('<ng-container *ngFor="let prepared of preparedSensors(); trackBy: prepared.sensor.id">') &&
|
||||||
|
result.includes('prepared.sensor.name') &&
|
||||||
|
!result.includes('@for');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 35: ControlFlowTransformer - @for z metodą obiektu w iterable
|
||||||
|
test('ControlFlowTransformer: @for z metodą obiektu w iterable', () => {
|
||||||
|
const transformer = new ControlFlowTransformer();
|
||||||
|
const input = '@for (item of data.getItems(); track $index) { <span>{{ item }}</span> }';
|
||||||
|
const result = transformer.transform(input);
|
||||||
|
return result.includes('<ng-container *ngFor="let item of data.getItems(); trackBy: $index">') &&
|
||||||
|
!result.includes('@for');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 36: ControlFlowTransformer - @for z wieloma zagnieżdżonymi nawiasami
|
||||||
|
test('ControlFlowTransformer: @for z wieloma zagnieżdżonymi nawiasami', () => {
|
||||||
|
const transformer = new ControlFlowTransformer();
|
||||||
|
const input = '@for (item of service.getData(filter(value())); track item.id) { <div>{{ item }}</div> }';
|
||||||
|
const result = transformer.transform(input);
|
||||||
|
return result.includes('<ng-container *ngFor="let item of service.getData(filter(value())); trackBy: item.id">') &&
|
||||||
|
!result.includes('@for');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 37: ControlFlowTransformer - @for z funkcją i złożonym trackBy
|
||||||
|
test('ControlFlowTransformer: @for z funkcją i złożonym trackBy', () => {
|
||||||
|
const transformer = new ControlFlowTransformer();
|
||||||
|
const input = '@for (range of prepared.sensor.ranges; track $index) { <div>{{ range.name }}</div> }';
|
||||||
|
const result = transformer.transform(input);
|
||||||
|
return result.includes('<ng-container *ngFor="let range of prepared.sensor.ranges; trackBy: $index">') &&
|
||||||
|
result.includes('range.name') &&
|
||||||
|
!result.includes('@for');
|
||||||
|
});
|
||||||
|
|
||||||
console.log('\n=== PODSUMOWANIE ===');
|
console.log('\n=== PODSUMOWANIE ===');
|
||||||
console.log(`✅ Testy zaliczone: ${passedTests}`);
|
console.log(`✅ Testy zaliczone: ${passedTests}`);
|
||||||
console.log(`❌ Testy niezaliczone: ${failedTests}`);
|
console.log(`❌ Testy niezaliczone: ${failedTests}`);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue