Skip to content

Commit

Permalink
feat(process-content): ability to add information to a data object (#…
Browse files Browse the repository at this point in the history
…1925)

* refactor(compiler): remove special treatment for au slot

* [skip ci]
  • Loading branch information
bigopon committed Mar 14, 2024
1 parent 6727b56 commit 2a4c436
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 112 deletions.
14 changes: 7 additions & 7 deletions packages/__tests__/src/3-runtime-html/au-slot.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -477,13 +477,13 @@ describe('3-runtime-html/au-slot.spec.tsx', function () {
[
MyElement,
],
{ 'my-element': [`<h4>First Name</h4> <h4>Last Name</h4> <div>John</div> <div>Doe</div> <div>Max</div> <div>Mustermann</div>`, new AuSlotsInfo([])] },
{ 'my-element': [`<h4>First Name</h4><h4>Last Name</h4> <div>John</div><div>Doe</div> <div>Max</div><div>Mustermann</div>`, new AuSlotsInfo([])] },
async function ({ app, host, platform }) {
app.people.push(new Person('Jane', 'Doe', []));
platform.domWriteQueue.flush();
assert.html.innerEqual(
'my-element',
`<h4>First Name</h4> <h4>Last Name</h4> <div>John</div> <div>Doe</div> <div>Max</div> <div>Mustermann</div> <div>Jane</div> <div>Doe</div>`,
`<h4>First Name</h4><h4>Last Name</h4> <div>John</div><div>Doe</div> <div>Max</div><div>Mustermann</div> <div>Jane</div><div>Doe</div>`,
'my-element.innerHTML',
host);
}
Expand Down Expand Up @@ -1079,7 +1079,7 @@ describe('3-runtime-html/au-slot.spec.tsx', function () {
CollVwr,
MyElement,
],
{ 'my-element': ['<h4>First Name</h4> <h4>Last Name</h4> <h4>Pets</h4> <div>John</div> <div>Doe</div> <coll-vwr><div>Browny</div><div>Smokey</div></coll-vwr> <div>Max</div> <div>Mustermann</div> <coll-vwr><div>Sea biscuit</div><div>Swift Thunder</div></coll-vwr>', new AuSlotsInfo([])] },
{ 'my-element': ['<h4>First Name</h4><h4>Last Name</h4><h4>Pets</h4> <div>John</div><div>Doe</div><coll-vwr><div>Browny</div><div>Smokey</div></coll-vwr> <div>Max</div><div>Mustermann</div><coll-vwr><div>Sea biscuit</div><div>Swift Thunder</div></coll-vwr>', new AuSlotsInfo([])] },
);

yield new TestData(
Expand All @@ -1098,7 +1098,7 @@ describe('3-runtime-html/au-slot.spec.tsx', function () {
MyElement,
],
{ 'my-element': [
'<h4>First Name</h4> <h4>Last Name</h4> <h4>Pets</h4> <div>John</div> <div>Doe</div> <coll-vwr><ul><li>Browny</li><li>Smokey</li></ul></coll-vwr> <div>Max</div> <div>Mustermann</div> <coll-vwr><ul><li>Sea biscuit</li><li>Swift Thunder</li></ul></coll-vwr>',
'<h4>First Name</h4><h4>Last Name</h4><h4>Pets</h4> <div>John</div> <div>Doe</div> <coll-vwr><ul><li>Browny</li><li>Smokey</li></ul></coll-vwr> <div>Max</div> <div>Mustermann</div> <coll-vwr><ul><li>Sea biscuit</li><li>Swift Thunder</li></ul></coll-vwr>',
new AuSlotsInfo(['content'])
]},
);
Expand All @@ -1119,7 +1119,7 @@ describe('3-runtime-html/au-slot.spec.tsx', function () {
CollVwr,
MyElement,
],
{ 'my-element': ['<h4>First Name</h4> <h4>Last Name</h4> <h4>Pets</h4> <div>John</div> <div>Doe</div> <coll-vwr><ul><li>Browny</li><li>Smokey</li></ul></coll-vwr> <div>Max</div> <div>Mustermann</div> <coll-vwr><ul><li>Sea biscuit</li><li>Swift Thunder</li></ul></coll-vwr>', new AuSlotsInfo(['content'])] },
{ 'my-element': ['<h4>First Name</h4><h4>Last Name</h4><h4>Pets</h4> <div>John</div> <div>Doe</div> <coll-vwr><ul><li>Browny</li><li>Smokey</li></ul></coll-vwr> <div>Max</div> <div>Mustermann</div> <coll-vwr><ul><li>Sea biscuit</li><li>Swift Thunder</li></ul></coll-vwr>', new AuSlotsInfo(['content'])] },
);

yield new TestData(
Expand All @@ -1140,7 +1140,7 @@ describe('3-runtime-html/au-slot.spec.tsx', function () {
],
{
'my-element': [
'<h4>First Name</h4> <h4>Last Name</h4> <h4>Pets</h4> <div>John</div> <div>Doe</div> <coll-vwr><ul><li>Browny</li><li>Smokey</li></ul></coll-vwr> <div>Max</div> <div>Mustermann</div> <coll-vwr><ul><li>Sea biscuit</li><li>Swift Thunder</li></ul></coll-vwr>',
'<h4>First Name</h4><h4>Last Name</h4><h4>Pets</h4> <div>John</div> <div>Doe</div> <coll-vwr><ul><li>Browny</li><li>Smokey</li></ul></coll-vwr> <div>Max</div> <div>Mustermann</div> <coll-vwr><ul><li>Sea biscuit</li><li>Swift Thunder</li></ul></coll-vwr>',
new AuSlotsInfo(['content'])
],
}
Expand All @@ -1156,7 +1156,7 @@ describe('3-runtime-html/au-slot.spec.tsx', function () {
CollVwr,
MyElement,
],
{ 'my-element': ['<h4>First Name</h4> <h4>Last Name</h4> <h4>Pets</h4> <div>John</div> <div>Doe</div> <coll-vwr><div>Browny</div><div>Smokey</div></coll-vwr> <div>Max</div> <div>Mustermann</div> <coll-vwr><div>Sea biscuit</div><div>Swift Thunder</div></coll-vwr>', new AuSlotsInfo(['colleslawt'])] },
{ 'my-element': ['<h4>First Name</h4><h4>Last Name</h4><h4>Pets</h4> <div>John</div><div>Doe</div><coll-vwr><div>Browny</div><div>Smokey</div></coll-vwr> <div>Max</div><div>Mustermann</div><coll-vwr><div>Sea biscuit</div><div>Swift Thunder</div></coll-vwr>', new AuSlotsInfo(['colleslawt'])] },
);

yield new TestData(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('3-runtime-html/template-compiler.au-slot.spec.ts', function () {
it('compiles default <au-slot> as the only child', function () {
const { template, instructions } = compileWith('<au-slot></au-slot>');
assertTemplateEqual(template, '<!--au*--><!--au-start--><!--au-end-->');
assertAuSlotFallback(instructions[0][0], { template: '', instructions: [] });
assertAuSlotFallback(instructions[0][0], null);
});

it('compiles 2 default <au-slot>s', function () {
Expand All @@ -56,8 +56,8 @@ describe('3-runtime-html/template-compiler.au-slot.spec.ts', function () {
template,
'<!--au*--><!--au-start--><!--au-end--><!--au*--><!--au-start--><!--au-end-->'
);
assertAuSlotFallback(instructions[0][0], { template: '', instructions: [] });
assertAuSlotFallback(instructions[1][0], { template: '', instructions: [] });
assertAuSlotFallback(instructions[0][0], null);
assertAuSlotFallback(instructions[1][0], null);
});

it('compiles default <au-slot> with fallback', function () {
Expand Down Expand Up @@ -95,20 +95,20 @@ describe('3-runtime-html/template-compiler.au-slot.spec.ts', function () {
it('compiles together with slot', function () {
const { template, instructions } = compileWith('<slot></slot><au-slot></au-slot>');
assertTemplateEqual(template, '<slot></slot><!--au*--><!--au-start--><!--au-end-->');
assertAuSlotFallback(instructions[0][0], { template: '', instructions: [] });
assertAuSlotFallback(instructions[0][0], null);
});

it('compiles named <au-slot>', function () {
const { template, instructions } = compileWith('<au-slot name="s1"></au-slot>');
assertTemplateEqual(template, '<!--au*--><!--au-start--><!--au-end-->');
assertAuSlotFallback(instructions[0][0], { name: 's1', template: '', instructions: [] });
assertAuSlotFallback(instructions[0][0], null);
});

it('compiles default <au-slot> mixed with named <au-slot>', function () {
const { template, instructions } = compileWith('<au-slot name="s1"></au-slot><au-slot></au-slot>');
assertTemplateEqual(template, '<!--au*--><!--au-start--><!--au-end--><!--au*--><!--au-start--><!--au-end-->');
assertAuSlotFallback(instructions[0][0], { name: 's1', template: '', instructions: [] });
assertAuSlotFallback(instructions[1][0], { template: '', instructions: [] });
assertAuSlotFallback(instructions[0][0], null);
assertAuSlotFallback(instructions[1][0], null);
});

it('compiles projection with default [au-slot]', function () {
Expand Down Expand Up @@ -150,7 +150,7 @@ describe('3-runtime-html/template-compiler.au-slot.spec.ts', function () {
$createCustomElement('', 'el')
);
assertTemplateEqual(template, '<!--au*--><!--au-start--><!--au-end--><!--au*--><el></el>');
assertAuSlotFallback(instructions[0][0], { template: '', instructions: [] });
assertAuSlotFallback(instructions[0][0], null);
assertProjection(instructions[1][0], { default: {
template: '<div></div>', instructions: []
} });
Expand Down Expand Up @@ -181,7 +181,7 @@ describe('3-runtime-html/template-compiler.au-slot.spec.ts', function () {

it('compiles projection that has <au-slot>', function () {
const { template, instructions } = compileWith(
'<el><au-slot au-slot">',
'<el><au-slot au-slot>',
$createCustomElement('', 'el')
);
assertTemplateEqual(template, '<!--au*--><el></el>');
Expand All @@ -190,7 +190,7 @@ describe('3-runtime-html/template-compiler.au-slot.spec.ts', function () {
} });
assertAuSlotFallback(
(instructions[0][0] as HydrateElementInstruction).projections.default.instructions[0][0],
{ template: '', instructions: [] }
null,
);
});

Expand Down Expand Up @@ -313,18 +313,20 @@ describe('3-runtime-html/template-compiler.au-slot.spec.ts', function () {

type HEI = HydrateElementInstruction;
const allInstructions = compiledDefinition.instructions.flat();

for (const expectedSlotInfo of expectedSlotInfos) {
const actualInstruction = allInstructions.find((i) =>
i.type === InstructionType.hydrateElement
&& (typeof (i as HEI).res === 'string' && ((i as HEI).res as string).includes('au-slot')
&& ((i as HEI).res === 'au-slot'
|| (i as HEI).res === CustomElement.getDefinition(AuSlot)
)
&& (i as HydrateElementInstruction).auSlot.name === expectedSlotInfo.slotName
&& expectedSlotInfo.slotName === (i as HEI).data.name
) as HydrateElementInstruction;
assert.notEqual(actualInstruction, void 0, 'instruction');
const actualSlotInfo = actualInstruction.auSlot;
assert.deepStrictEqual((actualSlotInfo.fallback.template as HTMLElement).outerHTML, `<template>${expectedSlotInfo.content}</template>`, 'content');
assert.deepStrictEqual(actualSlotInfo.fallback.needsCompile, false, 'needsCompile');

const slotFallback = actualInstruction.projections?.default;
assert.deepStrictEqual((slotFallback?.template as HTMLElement)?.outerHTML, `<template>${expectedSlotInfo.content}</template>`, 'content');
assert.deepStrictEqual(slotFallback?.needsCompile, false, 'needsCompile');
}

// for each element instruction found
Expand Down Expand Up @@ -384,16 +386,23 @@ describe('3-runtime-html/template-compiler.au-slot.spec.ts', function () {

function assertAuSlotFallback(
instruction: IInstruction,
{
expectedAuslotFallback: null | Pick<CustomElementDefinition, 'template' | 'instructions'> & { name?: string },
message?: string,
) {
const $auslotInstruction = instruction as HydrateElementInstruction;
if (expectedAuslotFallback === null) {
assert.strictEqual($auslotInstruction.projections, null, `<au-slot>.projections === null`);
return;
}

const {
name: expectedName = 'default',
template: expectedTemplate,
instructions: expectedInstructions
}: Pick<CustomElementDefinition, 'template' | 'instructions'> & { name?: string },
message?: string,
) {
const $instruction = instruction as HydrateElementInstruction;
const { name, fallback: { template, instructions } } = $instruction.auSlot ?? {};
assert.strictEqual($instruction.type, InstructionType.hydrateElement, `#instruction.type ${message}`);
} = expectedAuslotFallback ?? {};

const { data: { name }, projections: { default: { template, instructions } } = { default: {} } } = $auslotInstruction;
assert.strictEqual($auslotInstruction.type, InstructionType.hydrateElement, `#instruction.type ${message}`);
assert.strictEqual(name, expectedName, `#fallback.slotname ${message}`);
assertTemplateEqual(template, expectedTemplate, `#fallback.template ${message}`);
if (expectedInstructions !== anything) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -878,10 +878,10 @@ describe('3-runtime-html/template-compiler.spec.ts', function () {
type: TT.hydrateElement,
res: tagNameOrDef,
props: childInstructions as IInstruction[],
auSlot: null,
containerless: false,
projections: null,
captures: [],
data: {},
};
const def = typeof tagNameOrDef === 'string'
? CustomElement.find(ctx.container, tagNameOrDef)
Expand Down
75 changes: 12 additions & 63 deletions packages/runtime-html/src/compiler/template-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import type { AnyBindingExpression } from '@aurelia/runtime';
import type { CustomAttributeDefinition } from '../resources/custom-attribute';
import type { PartialCustomElementDefinition } from '../resources/custom-element';
import type { ICompliationInstruction, IInstruction, } from '../renderer';
import type { IAuSlotProjections } from '../templating/controller.projection';
import { auslotAttr, defaultSlotName, type IAuSlotProjections } from '../templating/controller.projection';
import { ErrorNames, createMappedError } from '../errors';

export class TemplateCompiler implements ITemplateCompiler {
Expand Down Expand Up @@ -591,8 +591,6 @@ export class TemplateCompiler implements ITemplateCompiler {
// plain attrs with bindings -> list 3
// el bindables -> list 4
// 2. ensure element instruction is present
// 2.1.
// if element is an <au-slot/> compile its content into auSlot property of the element instruction created
// 3. sort instructions:
// hydrate custom element instruction
// hydrate custom attribute instructions
Expand Down Expand Up @@ -676,6 +674,7 @@ export class TemplateCompiler implements ITemplateCompiler {
let hasContainerless = false;
let canCapture = false;
let needsMarker = false;
let elementMetadata: Record<PropertyKey, unknown>;

if (elName === 'slot') {
if (context.root.def.shadowOptions == null) {
Expand All @@ -684,9 +683,10 @@ export class TemplateCompiler implements ITemplateCompiler {
context.root.hasSlot = true;
}
if (isCustomElement) {
elementMetadata = {};
// todo: this is a bit ... powerful
// maybe do not allow it to process its own attributes
processContentResult = elDef.processContent?.call(elDef.Type, el, context.p);
processContentResult = elDef.processContent?.call(elDef.Type, el as HTMLElement, context.p, elementMetadata);
// might have changed during the process
attrs = el.attributes;
ii = attrs.length;
Expand Down Expand Up @@ -725,7 +725,7 @@ export class TemplateCompiler implements ITemplateCompiler {
continue;
}

canCapture = realAttrTarget !== AU_SLOT && realAttrTarget !== 'slot';
canCapture = realAttrTarget !== auslotAttr && realAttrTarget !== 'slot';
if (canCapture) {
bindablesInfo = BindablesInfo.from(elDef, false);
// if capture is on, capture everything except:
Expand Down Expand Up @@ -934,60 +934,12 @@ export class TemplateCompiler implements ITemplateCompiler {
// example: AOT/runtime can use def.Type, but there are situation
// where instructions need to be serialized, def.name should be used
this.resolveResources ? elDef : elDef.name,
void 0,
(elBindableInstructions ?? emptyArray) as IInstruction[],
null,
hasContainerless,
captures,
elementMetadata!,
);

// 2.1 prepare fallback content for <au-slot/>
if (elName === AU_SLOT) {
const slotName = el.getAttribute('name') || /* name="" is the same with no name */DEFAULT_SLOT_NAME;
const template = context.t();
const fallbackContentContext = context._createChild();
let node: Node | null = el.firstChild;
let count = 0;
while (node !== null) {
// a special case:
// <au-slot> doesn't have its own template
// so anything attempting to project into it is discarded
// doing so during compilation via removing the node,
// instead of considering it as part of the fallback view
if (isElement(node) && node.hasAttribute(AU_SLOT)) {
if (__DEV__) {
// eslint-disable-next-line no-console
console.warn(
`[DEV:aurelia] detected [au-slot] attribute on a child node`,
`of an <au-slot> element: "<${node.nodeName} au-slot>".`,
`This element will be ignored and removed`
);
}
el.removeChild(node);
} else {
appendToTemplate(template, node);
count++;
}
node = el.firstChild;
}

if (count > 0) {
this._compileNode(template.content, fallbackContentContext);
}

elementInstruction.auSlot = {
name: slotName,
fallback: CustomElementDefinition.create({
name: generateElementName(),
template,
instructions: fallbackContentContext.rows,
needsCompile: false,
}),
};
// todo: shouldn't have to eagerly replace everything like this
// this is a leftover refactoring work from the old binder
// el = this._replaceByMarker(el, context);
}
}

// 3. merge and sort all instructions into a single list
Expand Down Expand Up @@ -1073,18 +1025,18 @@ export class TemplateCompiler implements ITemplateCompiler {
let isEmptyTextNode = false;
if (processContentResult !== false) {
while (child !== null) {
targetSlot = isElement(child) ? child.getAttribute(AU_SLOT) : null;
targetSlot = isElement(child) ? child.getAttribute(auslotAttr) : null;
hasAuSlot = targetSlot !== null || isCustomElement && !isShadowDom;
childEl = child.nextSibling as Element;
if (hasAuSlot) {
if (!isCustomElement) {
throw createMappedError(ErrorNames.compiler_au_slot_on_non_element, targetSlot, elName);
}
(child as Element).removeAttribute?.(AU_SLOT);
(child as Element).removeAttribute?.(auslotAttr);
// ignore all whitespace
isEmptyTextNode = isTextNode(child) && child.textContent!.trim() === '';
if (!isEmptyTextNode) {
((slotTemplateRecord ??= {})[targetSlot || DEFAULT_SLOT_NAME] ??= []).push(child);
((slotTemplateRecord ??= {})[targetSlot || defaultSlotName] ??= []).push(child);
}
el.removeChild(child);
}
Expand Down Expand Up @@ -1263,18 +1215,18 @@ export class TemplateCompiler implements ITemplateCompiler {
// </my-el>
if (processContentResult !== false) {
while (child !== null) {
targetSlot = isElement(child) ? child.getAttribute(AU_SLOT) : null;
targetSlot = isElement(child) ? child.getAttribute(auslotAttr) : null;
hasAuSlot = targetSlot !== null || isCustomElement && !isShadowDom;
childEl = child.nextSibling as Element;
if (hasAuSlot) {
if (!isCustomElement) {
throw createMappedError(ErrorNames.compiler_au_slot_on_non_element, targetSlot, elName);
}
(child as Element).removeAttribute?.(AU_SLOT);
(child as Element).removeAttribute?.(auslotAttr);
// ignore all whitespace
isEmptyTextNode = isTextNode(child) && child.textContent!.trim() === '';
if (!isEmptyTextNode) {
((slotTemplateRecord ??= {})[targetSlot || DEFAULT_SLOT_NAME] ??= []).push(child);
((slotTemplateRecord ??= {})[targetSlot || defaultSlotName] ??= []).push(child);
}
el.removeChild(child);
}
Expand Down Expand Up @@ -1991,9 +1943,6 @@ export const templateCompilerHooks = <T extends Constructable>(target?: T) => {
}
/* eslint-enable */

const DEFAULT_SLOT_NAME = 'default';
const AU_SLOT = 'au-slot';

_START_CONST_ENUM();
const enum Char {
// Null = 0x00,
Expand Down
1 change: 1 addition & 0 deletions packages/runtime-html/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ export {
type CustomElementType,
CustomElementDefinition,
type PartialCustomElementDefinition,
type ProcessContentHook,
useShadowDOM,
processContent,
} from './resources/custom-element';
Expand Down
Loading

0 comments on commit 2a4c436

Please sign in to comment.