The preferred way to place content inside a custom element as an input to the component?

I’m trying to develop a syntax highlighting web component and it will take content placed inside it and syntax highlight it. So something like this:

<fs-highlight data-line-numbers="true" data-language="html">
   any content here ....
</fs-highlight>

This is a prototype implementation and it gets and highlights the content within the element like this:

this._code = hljs.highlight(this.innerHTML.replace(/^s+/g, ''), {
        language: this.language,
      }).value;

And I’m wondering whether it’s correct / fine to use this.innerHTML to grab the content?

The implementation is not using a slot, so the content is not rendered within the element.

However in this demo, vite fails to compile the element. However I’m wondering if that’s just due to how the Stackblitz vite compilation works …(I put a non trivial amount of markup within the element to test it out…)?

Perhaps with local compilation and publishing this goes away … Thoughts?

Vue: ref does not create a reactive property

TestPage.vue:

<template>

  <div>
    <TestChild :dog = "this.parentDog"  />

    <div v-on:click="this.changeName" id="change-button">
      Change Parent Dog name
    </div>
  </div>

</template>

<script>
 import TestChild from "@/test/TestChild";
 export default {
  components: {TestChild },

  methods:  {
    changeName()  {
      this.parentDog.name = "Changed First Name";
      alert("Changed!");
    }
  },

   data() {
     return {
       parentDog: {
         name: "First Dog"
       }
     };
   },
};

</script>

<style>

#change-button  {
  margin: 2em;
  padding: 1em;

  background-color: green;
}

</style>

TestChild.vue:

<template>

  <div>{{this.betterDog.name}}</div>

</template>

<script>
import {ref} from 'vue';

export default {
  name: "TestChild",

  props:  {
    dog: Object
  },

  data() {
    return {
      betterDog: Object
    };
  },

  created() {
    this.betterDog = {};
    this.betterDog.name = ref(this.dog.name);
  }
};
</script>

The output before the green button is clicked:

enter image description here

The data for the TestPage:
enter image description here
The data for the TestChild:
enter image description here

The names of parentDog in TestPage and betterDog in TestChild are the same.

After the green button is clicked, the page output doesn’t change. The data changes as follows:

The data for the TestPage:
enter image description here
The data for the TestChild:
enter image description here

So after the button is clicked, the dog prop in the TestChild component reflects the change, but the betterDog data does not, despite using ref.

How do I make betterDog.name reactive as well? Without using watch on dog prop.

Is there a way in Javascript to modify the clipboard without the user clicking a button?

I’m working on a small script to streamline the process of re-formatting pasted text, particularly phone numbers. My goal is to paste the text into a textbox and have the formatted result automatically copied to my clipboard to minimize manual clicks.

The function to modify the clipboard only works when a real click event occurs. I’ve attempted various approaches such as programmatically calling .click(), dispatching a “click” event, utilizing async/await, and promises

JSFiddle Code:
https://jsfiddle.net/ByteBender/7bukf5xh/9/

document.getElementById('myInput').addEventListener('paste', event => {
    const contents = event.clipboardData.getData('text')
    if (contents.length >= 9) {
        event.preventDefault()
        const formattedNumber = formatPhoneNumber(contents)
        myInput.value = formattedNumber
        copyToClipboard()
        
      // Also failed to copy
      //myButton.click()
      // Also failed to copy
      //setTimeout( () => myButton.dispatchEvent(new Event('click')), 500 )
    }
});

function copyToClipboard() {
  var copyText = document.getElementById("myInput");
  copyText.select();
  copyText.setSelectionRange(0, 99999); // For mobile devices
  navigator.clipboard.writeText(copyText.value)
    .then(()=> console.log('success!'))
    .catch(err=>console.error(`fail: ${err}`))
}


// Also failed to copy
/* function tempButton(){
  var button = document.createElement('button');
  button.innerHTML = 'click me';
  button.onclick = function(){
    copyToClipboard();
        return false;
  };
  document.body.appendChild(button);
  //button.click()
  
} */

Invariant Violation: A state mutation was detected between dispatches, in the path

I would like to concatenate one item in the array with another one and remove the second item from the array. When I tried the old way, I get the state mutation detected error. When I tried the Object.assign, I am unable to get the values concatenated.

EDIT:
What is the equivalent of interests[makeIndex].value = ``${interests[makeIndex].value}, ${interests[secondPreferredMakeIndex].value}`` using Object.assign?

For e.g., for the below code segment the output I expect is,

Preferred Make - before Chevrolet
Preferred Make - after Chevrolet, Porsche
// Interests array from state
let interests = [
  {
    type: 'Preferred Make',
    value: 'Chevrolet',
  },
  {
    type: 'Second Preferred Make',
    value: 'Porsche',
  },
  {
    type: 'Preferred Model',
    value: 'Corvette',
  },
  {
    type: 'Second Preferred Model',
    value: 'Macan',
  }
];

console.log("Preferred Make - before", interests[0].value);

const secondPreferredMakeIndex = interests
  .map(x => x.type)
  .indexOf('Second Preferred Make');

if (secondPreferredMakeIndex > -1) {
  let makeIndex = interests.map(x => x.type).indexOf('Preferred Make');

  if (makeIndex > -1) {
    // For the below, I get mutation error. But it works
    // interests[makeIndex].value = `${interests[makeIndex].value}, ${interests[secondPreferredMakeIndex].value}`;

    // Concatenate and use Object.assign to avoid mutation
    interests = Object.assign([], interests, { makeIndex: `${interests[makeIndex].value}, ${interests[secondPreferredMakeIndex].value}` });
    /*
    // Tried the below as well in vain
    interests = Object.assign([], interests, {
      makeIndex: {
        type: 'Preferred Make',
        value: `${interests[makeIndex].value}, ${interests[secondPreferredMakeIndex].value}`
      },
    });
    */
  }

  // Delete the second Preferred Make
  interests.splice(secondPreferredMakeIndex, 1);
}

console.log("Preferred Make - after", interests[0].value);

Appreciate the helps

Timing and direction of keyframe animation for SVG

I have an svg that I’m using CSS keyframe on in order to animate it at a certain point and it animates, but the issue is that I need to time out the animations as well as make sure they ‘draw’ in the right direction. The ‘normal’ paths on the right need to draw left to right and the ‘reverse’ paths on the left need to draw right to left (this should end up like the lines, from top to bottom, are drawing center->out.

My issue is that I can’t seem to get a delay working on them so that they can draw properly. If I set the reverse keyframe to draw for a negative value of the path length, it draws the path all the way to the right before the right paths even show so that won’t work.

How can I make sure the reverse paths draw properly but also time the paths out and have the ability to adjust them as needed?

    document.addEventListener("DOMContentLoaded", function () {
        var drawingStarted = false;
        var lastScrollTop = 0;
        const normalPath1  = document.querySelector("#footerline1");
        const reversePath1 = document.querySelector("#footerline2");
        const normalPath2  = document.querySelector("#footerline3");
        const reversePath2 = document.querySelector("#footerline4");
        const normalPath3  = document.querySelector("#footerline5");
        const reversePath3 = document.querySelector("#footerline6");
        const normalPath4  = document.querySelector("#footerline7");
        const reversePath4 = document.querySelector("#footerline8");


        window.addEventListener('message', function (event) {

                var footerscrollpercent = event.data.value;
                
                // REMOVE DEBUG
                footerscrollpercent = 1;
                if (!drawingStarted && footerscrollpercent > 0.9) {
                    // Function to add class with delay
                    const addClassWithDelay = (element, className, delay) => {
                        setTimeout(() => {
                            element.classList.add(className);
                        }, delay);
                    };

                    // Add classes with delay
                    addClassWithDelay(normalPath1, 'path-test', 5000);
                    addClassWithDelay(reversePath1, 'path-test-reverse', 2000); // Delay of 5ms
                    addClassWithDelay(normalPath2, 'path-test', 0);
                    addClassWithDelay(reversePath2, 'path-test-reverse', 5); // Delay of 5ms
                    addClassWithDelay(normalPath3, 'path-test', 0);
                    addClassWithDelay(reversePath3, 'path-test-reverse', 5); // Delay of 5ms
                    addClassWithDelay(normalPath4, 'path-test', 0);
                    addClassWithDelay(reversePath4, 'path-test-reverse', 5); // Delay of 5ms
                }

            // }
        });
    });
.path-test {
    stroke-dasharray: 1000;
    stroke-dashoffset: 1000;
    animation: dash 5s linear forwards;
}

@keyframes dash {
    from {
            stroke-dashoffset: 1000;
            /* Start with full dash length */
        }
    to {
        stroke-dashoffset: 0;
    }
}

.path-test-reverse {
    stroke-dasharray: 1000;
    stroke-dashoffset: 1000;
    /* Start with the dash fully visible */
    animation: reverse-dash 5s linear forwards;
    /* Use the reverse-dash animation */
}

@keyframes reverse-dash {
    from {
        stroke-dashoffset: -10;
        /* Start with full dash length */
    }

    to {
        stroke-dashoffset: 0;
        /* Gradually offset the dash to 0 */
    }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<svg viewBox="0 0 758.57 337.81">
                <defs>
                    <style>
                        .complexfooter-1 {
                            stroke-width: 12.67px;
                        }
                        .complexfooter-1,
                        .complexfooter-2 {
                            fill: none;
                            stroke: #072334;
                            stroke-miterlimit: 10;
                        }
                        .complexfooter-3 {
                            fill: #072334;
                            stroke-width: 0px;
                        }
                        .complexfooter-2 {
                            stroke-linecap: square;
                            stroke-width: 23.8px;
                        }
                    </style>
                </defs>
                <path  id="footerline1" fill="white" stroke="black" stroke-width="4" class="normal-draw complexfooter-2"
                   d="M381.22,12.6c14.3.44,29.45.94,30.7.94l-12.26-1.5c2.36,0,4.95-.3,7.28-.03,2.08.24,3.02,1.17,4.97,1.54,5.7,1.08,12.66-.05,18.47-.05,17.5,0,35,0,52.51,0,19.74,0,39.31,1.02,59.01,1.39,4.79.09,9.24,1.49,14.02,1.54,2.68.03,5.78-.51,8.4.03,3.97.82,8.32,4.68,11.55,6.88,3.74,2.54,6.2,5.18,9.38,8.36,4.39,4.39,9.92,7.61,13.85,12.46,1.65,2.04,2.36,3.74,2.94,6.21,1.52,6.39,5.13,11.72,8.74,17.24,3.01,4.6,5.11,9.51,7.8,14.28,2.67,4.74,6.14,8.8,8.66,13.65,3.61,6.95,8.33,12.29,13.02,18.55,5.73,7.65,10.65,15.86,16.39,23.5,5.34,7.11,8.98,15.31,13.71,22.84,4.31,6.86,8.91,13.66,13.02,20.64s7.07,14.29,11.67,20.55c3.72,5.06,8.81,9.82,13.07,14.48,4.52,4.93,7.99,10.11,11.58,15.77,3.8,6,8.11,11.53,11.65,17.71,4.28,7.47,8.57,14.94,12.61,22.54,2.29,4.3,2.69,12.16,2.7,17.09" />
                <path  id="footerline2" fill="white" stroke="black" stroke-width="4" class=" reverse-draw complexfooter-2" fill="white" stroke="black" stroke-width="4"
                    d="M11.91,286.97c0-4.93,4.27-10.56,6.56-14.86,4.05-7.6,8.34-15.07,12.61-22.54,3.53-6.18,7.84-11.71,11.65-17.71,3.59-5.66,7.06-10.83,11.58-15.77,4.27-4.66,9.36-9.41,13.07-14.48,4.6-6.26,7.74-13.89,11.67-20.55,4.11-6.98,8.71-13.78,13.02-20.64,4.73-7.53,8.37-15.73,13.71-22.84,5.74-7.64,10.66-15.85,16.39-23.5,4.69-6.26,9.41-11.6,13.02-18.55,2.52-4.85,5.99-8.91,8.66-13.65,2.69-4.77,4.79-9.68,7.8-14.28,3.61-5.52,7.22-10.85,8.74-17.24.59-2.47,1.29-4.17,2.94-6.21,3.93-4.85,9.46-8.07,13.85-12.46,3.18-3.18,5.64-5.82,9.38-8.36,3.23-2.19,7.58-6.06,11.55-6.88,2.62-.54,5.72,0,8.4-.03,4.78-.05,9.23-1.45,14.02-1.54,19.7-.37,39.27-1.39,59.01-1.39,17.5,0,35,0,52.51,0,5.82,0,12.78,1.14,18.47.05,1.95-.37,2.89-1.3,4.97-1.54,1.1-.12,13.08.21,25.73.59" />
                <path  id="footerline3" fill="white" stroke="black" stroke-width="4" class="complexfooter-2"
                    d="M378.92,74.83c25.06.73,53.61,1.51,55.29.43,0,0-4.69,0,0,0,18.33.01,10.99,0,29.32,0,6.86,0,13.71.05,20.57-.03,5.58-.07,10.85,1.48,16.39,1.6,2.97.07,5.96-.4,8.89.21,9.14,1.89,17.67,6.32,23.72,13.25,3.96,4.54,8.26,8.94,12.4,13.32,3.12,3.29,5.41,7.06,8.27,10.57,7.73,9.47,14.82,19.88,21.08,30.45,4.39,7.41,8.11,15.17,12.64,22.49,5.84,9.44,11.28,20.06,18.07,28.81,5.62,7.26,9.76,15.31,14.41,23.26,7.04,12.03,14.28,23.66,22.44,34.98,6.67,9.25,12.92,18.72,19.3,28.16,4.92,7.28,13.14,14.47,11.65,23.91"/>
                <path  id="footerline4" fill="white" stroke="black" stroke-width="4" class="complexfooter-2"
                    d="M89.06,306.23c-1.49-9.43,6.73-16.62,11.65-23.91,6.38-9.44,12.63-18.92,19.3-28.16,8.16-11.31,15.4-22.95,22.44-34.98,4.66-7.96,8.79-16.01,14.41-23.26,6.79-8.76,12.23-19.37,18.07-28.81,4.53-7.32,8.25-15.08,12.64-22.49,6.26-10.57,13.36-20.98,21.08-30.45,2.86-3.51,5.15-7.27,8.27-10.57,4.14-4.38,8.44-8.78,12.4-13.32,6.05-6.94,14.58-11.37,23.72-13.25,2.93-.6,5.92-.14,8.89-.21,5.54-.12,10.81-1.67,16.39-1.6,6.86.09,13.71.03,20.57.03,18.33,0,10.99.01,29.32,0,4.69,0,8.59-.15,12.95-1.17,1.47-.35,18.59.19,37.76.75"/>
                <path  id="footerline5" fill="white" stroke="black" stroke-width="4" class="complexfooter-2"
                    d="M381.22,132.42c-26.3,0-56.96,0-61.4,0-5.9,0-12.03-.49-17.9-.06-3.02.22-5.93,1.32-8.97,1.94-3.3.67-6.74.74-9.47,2.2-2.17,1.16-4.65,3.01-6.58,4.47-5.98,4.52-8.82,11.16-14.19,15.68-2.84,2.38-3.99,5.49-6.03,8.37-2.91,4.11-5.92,8.05-8.47,12.44-5.17,8.9-8.84,18.4-13.47,27.54-2.29,4.52-5.74,8.16-8.16,12.57-5.76,10.51-10.63,22.21-15.13,33.31-3.97,9.8-8.46,18.38-14.44,27.06-4.89,7.1-18.8,34.09-17.53,43.23"/>
                <path  id="footerline6" fill="white" stroke="black" stroke-width="4" class="complexfooter-2"
                    d="M578.38,317.03c1.27-9.14-8.07-31.99-12.96-39.08-5.98-8.67-10.47-17.25-14.44-27.06-4.5-11.1-9.37-22.8-15.13-33.31-2.42-4.41-5.87-8.05-8.16-12.57-4.63-9.14-8.3-18.64-13.47-27.54-2.55-4.39-5.56-8.32-8.47-12.44-2.04-2.88-3.19-5.99-6.03-8.37-5.37-4.51-8.21-11.15-14.19-15.68-1.93-1.46-4.41-3.31-6.58-4.47-2.72-1.45-6.17-1.53-9.47-2.2-3.04-.62-5.95-1.71-8.97-1.94-5.87-.43-12.01.06-17.9.06-7.62,0-15.24,0-22.87,0-3.18,0-19.74,0-38.54,0"/>
                    <path  id="footerline7" fill="white" stroke="black" stroke-width="4" class="complexfooter-2"
                    d="M378.95,195.39c25.94-.12,51.45.02,55.27.02l-15.7-.8c7.82,0,16.53.19,23.52,2.54,3.82,1.29,7.21,2.52,9.5,6.08,2.99,4.66,4.92,10.38,7.56,15.3,5.14,9.56,11.6,17.28,14.57,28,2.37,8.53,6.51,15.55,10.15,23.52,1.27,2.77,1.77,5.83,3.08,8.56,1.39,2.89,3.62,5.66,5.22,8.41,4.96,8.54,7.97,28.36,7.6,38.44"/>
                    <path  id="footerline8" fill="white" stroke="black" stroke-width="4" class="complexfooter-2"
                    d="M262.68,324.92c-.37-10.08,2.68-29.36,7.63-37.9,1.6-2.75,3.83-5.52,5.22-8.41,1.31-2.73,1.82-5.79,3.08-8.56,3.64-7.98,7.78-15,10.15-23.52,2.98-10.73,9.44-18.44,14.57-28,2.64-4.92,4.57-10.63,7.56-15.3,2.29-3.57,5.68-4.8,9.5-6.08,3.58-1.21,31.3-1.63,58.54-1.76"/>
            </svg>

Title is not showing when uploading a video to TikTok via api

I am creating videos on TikTok using their Api. With the video, I also provide a title. The video is showing up in my Inbox for approval as expected, but the title is not showing. Instead I only get a HashTag of the name of the App I am using.

I checked the code a few times and made sure the json structure is correct. What am I missing?

 return await fetch('https://open.tiktokapis.com/v2/post/publish/inbox/video/init/', {
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${accessToken}`,
            'Content-Type': 'application/json; charset=UTF-8'
        },
        body: JSON.stringify({
            post_info: {
                privacy_level: 'PUBLIC_TO_EVERYONE',
                title: "Hello #world",
            },
            source_info: {
                source: 'PULL_FROM_URL',
                video_url: `THE VIDEO URL`
            }
        })
    })

Circuimventing third party cookie blocking

Outer website: outer.com; my website: my.com

Outer website wants to integrate my website using iframe.

I need to integrate auth as well. They use OpenID Connect or something like that.

When I tried to auth inside iframe using redirects, it didn’t work. Their auth logics using intermediate cookie and it was not saved.

Right now to authenticate user, I’m using code like parent.location.href = "outer.com/auth/redirect=my.com/accept-auth". After authentication I’m receiving request from the user, creating session cookie and issuing redirect to “outer.com”. outer.com loads my.com in the iframe and sends cookie set earlier. Now my website inside iframe is authorized and works well.

Please note that URL in the user browser changes in the following way: outer.com -> outer.com/auth/... -> my.com/accept-auth -> outer.com

Initially I had a problem: cookie set after redirect was not sent when page loaded inside iframe.

I solved this problem using SameSite=None cookie attribute.

Right now the website works well in the Chrome.

However after testing in Firefox and Chrome Incognito mode it turned out that this is not enough. Cookie is not send inside iframe.

After I did research, it turned out to be caused by a feature called “Third-party cookie blocking”.

I found an advice to use “Partitioned” cookie attribute. However further testing revealed that this approach does not work. It allows iframe to set cookie “inside”, however if cookie was set from the outside, it still is not accessible inside iframe.

I also tried localStorage as alternative way to store session id, but it didn’t work as well. It seems that browser maintains a separate localStorage object for iframe to prevent sharing data.

How do I proceed from there? I can ask user to disable this feature, but I’d like a cleaner solution.

Tick function in angular seems not working

I have simple test of mat-checkbox wrapper in Angular. I want to check if mat-ripple disappeared after blur event. Looks like fakeAsync in that case doesn’t work

it('test for checkbox', fakeAsync(() => {
  expect(fixture.nativeElement.querySelectorAll('.mat-ripple-element').length)
      .toBe(0, 'NO RIPPLE');

  // DISPATCH PART
  dispatchFakeEvent(inputElement, 'keydown');
  dispatchFakeEvent(inputElement, 'focus');

  tick(5000);

  expect(fixture.nativeElement.querySelectorAll('.mat-ripple-element').length)
      .toBe(1, 'RIPPLE VISABLE');

  dispatchFakeEvent(checkboxInstance._inputElement.nativeElement, 'blur');
  tick(15000); // here in normal after blur mat-ripple-element disappeared, but in test no

  expect(fixture.nativeElement.querySelectorAll('.mat-ripple-element').length)
      .toBe(0, 'RIPPLE NOT VISABLE');
}));

To check it I create simple function directly in component to trigger actions:

code from component

testActionMethod(){
  const input = document.querySelector('input');
  if (input) {
      const event1 = new KeyboardEvent('keydown');
      const event2 = new KeyboardEvent('focus');
      input.dispatchEvent(event1);
      input.dispatchEvent(event2);

      const classx = document.querySelectorAll('.mat-ripple-element')
      console.log(classx,'.mat-ripple-element exist?') // shows that exist

      const event3 = new KeyboardEvent('blur');
      input.dispatchEvent(event3);

      const classx12 = document.querySelectorAll('.mat-ripple-element')
      console.log(classx12,'mat-ripple-element exist?') // shows that exist

      setTimeout(() => {
          const classx1 = document.querySelectorAll('.mat-ripple-element')
          console.log(classx1,'mat-ripple-element exist?') //shows that doesn't exist
      },5000)
}

Of course after blur event when I just click element it works <mat-ripple-element does disappeared after blur, in settimeout also, but in test no>

anyone knows why it can behave like that?

How to check if an attribute that belongs to multiple elements has one of several values

I’m trying to test whether an attribute that is shared by 3 elements has one of several values. Here’s the html:


    <div class="wrapper">
      <a title="value 1"</a> 
      <a title="value 2"</a> 
      <a title="value 3"</a> 
    </div>

I need a way to assert that the title attribute of all three elements will have one of the 3 values, because their order might change in the future but I just need to make sure that they’re there.

Here’s what I’ve tried but didn’t work for me:

cy.get('.wrapper')
    .children().as('socialLinks');

cy.get('@socialLinks')
    .should('have.attr', 'title').as('title');

expect('@title').to.contain.oneOf('value 1', 'value 2', 'value 3');

I suspect it might need a for-loop but I can’t wrap my head around how to approach it.

Converting japanese fullwidth characters typed by user to halfwidth character in the same input field

There’s an input field, when the user will type any Fulwidth japanese character (numbers only) it won’t be displayed on that input field. It’ll be converted to it’s respective halfwidth character and, then that halfwidth character will be visible inside that same input field. I user type 0, this 0ill not be shown in the input field, it’ll show the converted halfwidth 0. In my case the problem is if the user type 0, the function is running twice and showing 00, but it should be only 0. Can anybody please help me to sort it out? i’m stucked here.

 <input type="text" id="inputField" oninput="convertToHalfWidth(this.value)">
<script>
        function convertToHalfWidth(input) {
            console.log(input);
            // Map of full-width to half-width numbers
            const fullToHalfMap = {
                '0': '0', '1': '1', '2': '2', '3': '3', '4': '4', '5': '5', '6': '6', '7': '7', '8': '8', '9': '9'
            };

            // Replacing full-width numbers with their half-width counterparts
            const convertedInput = input.replace(/[0-9]/g, match => fullToHalfMap[match]);

            // Updating the input field with the converted value
            document.getElementById('inputField').value = convertedInput;
        }
    </script> here

React Monorepo – Babel complaining about jsx not being enabled

I’m trying to create a monorepo that uses a Component from one subpackage in another subpackage, but no matter what I do, I get an error about jsx not being enabled within babel. I know the dependency tree is correct because if I console log a string export from the Component, it works. It’s only when I try using an actual Component does it crash.

I’ve tried everything I think of or find on Google searching. I’ve tried adding dependencies on all the babel packages, as well as different combinations of the babel files (babel.config.js, babel.config.json, .babelrc). I’m simply at a loss.

To test this, I downloaded this repo (in case the link gets removed, it’s on github, called “SugandSingh/MonoRepo_with_fullStackTodoList”) – This is just a simple monorepo that uses yarn, created fairly recently, that I found on a Medium Tutorial.

Then under packages/shared I created a Components.js file:

export const SomeComponent = () => {
  console.log("SomeComponent");
  // This does not work
  return <p>SomeComponent</p>;
  // return "SomeComponent"; <-- this works
}

Then I created an index.js file:

export * from './Components'

Finally, from the project root directory, I ran:

yarn install
yarn shared-build
yarn web-start

My versions:

yarn: 3.8.1,
node: 20.11.0
npm: 10.2.4

Error when booting up:

SyntaxError: <local path>packagessharedComponents.js: Support for the experimental syntax 'jsx' isn't currently enabled (4:10):
  2 |   console.log("SomeComponent");
  3 |   // This does not work
> 4 |   return <p>SomeComponent</p>;
    |          ^
  5 |   // return "SomeComponent"; <-- this works
  6 | }

Add @babel/preset-react (https://github.com/babel/babel/tree/main/packages/babel-preset-react) to the 'presets' section of your Babel config to enable transformation.
If you want to leave it as-is, add @babel/plugin-syntax-jsx (https://github.com/babel/babel/tree/main/packages/babel-plugin-syntax-jsx) to the 'plugins' section to enable parsing.

If you already added the plugin for this syntax to your config, it's possible that your config isn't being loaded.
You can re-run Babel with the BABEL_SHOW_CONFIG_FOR environment variable to show the loaded configuration:
        npx cross-env BABEL_SHOW_CONFIG_FOR=<local path>packagessharedComponents.js <your build command>
See https://babeljs.io/docs/configuration#print-effective-configs for more info.

Closed before a response was received in chrome extension content.js background.js interaction

I’m trying to communicate from content.js to background.js to save some data to the chrome.storage.local but I can’t figure out how to do it correctly.
I’m not very familiar with js so forgive me if it’s a stupid question.
That’s the code:

content.js

function sendMessage(type) {
  chrome.runtime.sendMessage({ action: type }, (response) => {
    console.log(response);
  });
}

sendMessage("test");

background.js

// Listen for messages from content scripts
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.action === "test") {
    saveData(sendResponse);
  }
  return true;
});

// Function to save data to Chrome local storage
function saveData(sendResponse) {
  // Sample data to save
  const test = ["test1", "test2", "test3"];
  chrome.storage.local.set({ test: test }, () => {
  sendResponse("saved");
  });
}

result:

undefined
Unchecked runtime.lastError: The message port closed before a response was received.

What I’m doing wrong?

Javascript Intl.NumberFormat output problem

The function below produces output as “d59”.
The parameters this function takes are “de-De”, “eur” and 59

export function formatCurrency(loc: string, curr: string, price: number): string {
      let currUp = curr.toUpperCase(); //output: EUR
      return new Intl.NumberFormat(loc, { style: 'currency', currency: currUp }).format(price);
}

Output “d59”

What’s wrong here?