11ty / eleventy-plugin-webc

Adds support for WebC *.webc files to Eleventy
https://www.11ty.dev/docs/languages/webc/
120 stars 10 forks source link

Nested web components causing (or contributing to) fatal error #99

Open mirisuzanne opened 3 months ago

mirisuzanne commented 3 months ago

😱 FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

(don't worry, I survived)

Full error output ``` <--- Last few GCs ---> [52368:0x138008000] 140133 ms: Mark-sweep 4052.3 (4140.6) -> 4038.6 (4142.6) MB, 1920.6 / 0.0 ms (average mu = 0.142, current mu = 0.005) allocation failure; scavenge might not succeed [52368:0x138008000] 142738 ms: Mark-sweep 4054.4 (4142.6) -> 4040.8 (4144.9) MB, 2596.4 / 0.0 ms (average mu = 0.067, current mu = 0.003) allocation failure; scavenge might not succeed <--- JS stacktrace ---> FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory 1: 0x10075978c node::Abort() [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 2: 0x100759970 node::ModifyCodeGenerationFromStrings(v8::Local, v8::Local, bool) [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 3: 0x1008b01ac v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 4: 0x100a5ab64 v8::internal::EmbedderStackStateScope::EmbedderStackStateScope(v8::internal::Heap*, v8::internal::EmbedderStackStateScope::Origin, cppgc::EmbedderStackState) [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 5: 0x100a5950c v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 6: 0x100a4d5a0 v8::internal::HeapAllocator::AllocateRawWithLightRetrySlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 7: 0x100a4dde4 v8::internal::HeapAllocator::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 8: 0x100a33d64 v8::internal::Factory::NewFillerObject(int, v8::internal::AllocationAlignment, v8::internal::AllocationType, v8::internal::AllocationOrigin) [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 9: 0x100dd44cc v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 10: 0x10112110c Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 11: 0x101165744 Builtins_PerformPromiseThen [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 12: 0x1010dda08 Builtins_AsyncFunctionAwaitUncaught [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 13: 0x10628aba8 14: 0x1010ddef4 Builtins_AsyncFunctionAwaitResolveClosure [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 15: 0x10116c838 Builtins_PromiseFulfillReactionJob [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 16: 0x1010cfc4c Builtins_RunMicrotasks [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 17: 0x1010aa3a4 Builtins_JSRunMicrotasksEntry [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 18: 0x1009dc92c v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 19: 0x1009dce1c v8::internal::(anonymous namespace)::InvokeWithTryCatch(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 20: 0x1009dcff8 v8::internal::Execution::TryRunMicrotasks(v8::internal::Isolate*, v8::internal::MicrotaskQueue*, v8::internal::MaybeHandle*) [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 21: 0x100a037e4 v8::internal::MicrotaskQueue::RunMicrotasks(v8::internal::Isolate*) [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 22: 0x100a03f80 v8::internal::MicrotaskQueue::PerformCheckpoint(v8::Isolate*) [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 23: 0x10069cc4c node::InternalCallbackScope::Close() [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 24: 0x10069c7c4 node::InternalCallbackScope::~InternalCallbackScope() [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 25: 0x1007b7280 node::PerIsolatePlatformData::RunForegroundTask(std::__1::unique_ptr>) [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 26: 0x1007b5f94 node::PerIsolatePlatformData::FlushForegroundTasksInternal() [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 27: 0x10108c5d4 uv__async_io [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 28: 0x10109eb88 uv__io_poll [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 29: 0x10108caa4 uv_run [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 30: 0x10069d6e0 node::SpinEventLoop(node::Environment*) [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 31: 0x100795e20 node::NodeMainInstance::Run() [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 32: 0x100726328 node::LoadSnapshotDataAndRun(node::SnapshotData const**, node::InitializationResult const*) [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 33: 0x1007265e0 node::Start(int, char**) [/Users/mia/.nvm/versions/node/v18.20.2/bin/node] 34: 0x198ad20e0 start [/usr/lib/dyld] [1] 52346 abort npm run dev ```

I understand that this sort of thing is likely caused by pagination. And I am using data-pagination to generate open-graph images for every page on the site. I suppose at some point that could be a lot of pages (and I should re-consider?) – but at this point, it's in the low 20-some test pages. But I wasn't messing with pagination at the time, so the issue started somewhere else - even if pagination lead to the crash.

I am using:

It seems like the cause was an attempt at nesting two webc custom elements. Three files were involved, roughly like this:

<!-- a-layout-file.webc -->
<desired-component with-attributes>Slotted text</desired-component>
<!-- _includes/desired-component.webc -->
<base-component webc:nokeep>
  <slot>midway slot defaults</slot>
</base-component>

<script>
class DesiredComponent extends BaseComponent { /* etc */ }
</script>
<!-- _includes/base-component.webc -->
<button more-stuff>
  <slot>base slot defaults</slot>
</button>

<script>
class BaseComponent extends HTMLElement { /* etc */ }
</script>

I wanted both:

I probably tried a number of other things in there. Adding webc:root various places, to try and get the desired outcome. I don't remember the exact state when things broke.

In the end, I split the base component into two files: a JS file with the registration, and a webc file with the template. Then I import the JS file in both components. I also duplicated the template content - it's not enough to worry about duplication.

mirisuzanne commented 3 months ago

Ok, I'm getting this error again – and the reason seems similar. Here's the situation:

In my template, I'm using the <black-out> component:

I want to <black-out>pass in some slotted content</black-out>.

black-out.webc is really just a shorthand to set up another webC web component with the proper attributes, and get some necessary styles in place:

<toggle-switch
  data-for="#main"
  data-set="data-erase"
><slot>(erase it)</slot></toggle-switch>

<style webc:bucket="object">
#main[data-erase] {
  visibility: hidden;
}

[data-set=data-erase],
mark { visibility: visible; }
</style>

toggle-switch.webc is a fully registered web component, that also has a slot:

<button
  type="button"
  :data-btn="this.dataBtn || 'switch'"
  :aria-pressed="!!this.ariaPressed"
><slot>toggle</slot></button>

<script src="./toggle-switch.js"></script>

The error only occurs when I add the <slot>…</slot> in black-out.webc. If I remove the middle-step slot, everything works great – but without any way to pass in content. If I add the slot, I get this fatal error.

mirisuzanne commented 3 months ago

To take the web component out of the equation, I created a test-it.webc component, which only has:

<button>hello <slot></slot></button>

Then redirected black-out.webc to wrap test-it:

<test-it><slot></slot></test-it>

That breaks too. It seems like nested slots cause the issue?