<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: shubhadip</title>
    <description>The latest articles on DEV Community by shubhadip (@shubhadip).</description>
    <link>https://dev.to/shubhadip</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F466678%2Fcf2c0121-305d-401a-b6f3-339f1fe9fccf.png</url>
      <title>DEV Community: shubhadip</title>
      <link>https://dev.to/shubhadip</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shubhadip"/>
    <language>en</language>
    <item>
      <title>Audio to Text Input via Google Speech to Text</title>
      <dc:creator>shubhadip</dc:creator>
      <pubDate>Sun, 20 Oct 2024 05:48:53 +0000</pubDate>
      <link>https://dev.to/shubhadip/audio-to-text-input-via-google-speech-to-text-4ob0</link>
      <guid>https://dev.to/shubhadip/audio-to-text-input-via-google-speech-to-text-4ob0</guid>
      <description>&lt;p&gt;In this article we will look into following topics&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;navigator.mediaDevices.getUserMedia browser Api&lt;/li&gt;
&lt;li&gt;google speech to text Api&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;we will start by creating react hook which will be doing all the stuffs like startRecording, stopRecording, creating Audio Blob, error handling etc. &lt;/p&gt;

&lt;p&gt;There are few other things to take care of before we get into the meat of the hook&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Minimum decibel above which we would consider a dialogue as input eg -35db (just an random number)&lt;/li&gt;
&lt;li&gt;How long should be the pause that would indicate that user has stopped the input e.g 2000ms
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const VOICE_MIN_DECIBELS = -35
const DELAY_BETWEEN_DIALOGUE = 2000

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets name our hook as &lt;code&gt;useAudioInput.ts&lt;/code&gt; we would be using the browser apis like navigator.mediaDevices.getUserMedia, MediaRecorder and AudioContext. AudioContext will help us identify whether the input audio is higher than the minimum decibel that is required for it to be considered as input, so we would start with following variables and props&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const defaultConfig = {
    audio: true
};

type Payload = Blob;

type Config = {
    audio: boolean;
    timeSlice?: number
    timeInMillisToStopRecording?: number
    onStop: () =&amp;gt; void;
    onDataReceived: (payload: Payload) =&amp;gt; void
};

export const useAudioInput = (config: Config = defaultConfig) =&amp;gt; {
    const mediaChunks = useRef&amp;lt;Blob[]&amp;gt;([]);
    const [isRecording, setIsRecording] = useState(false);
    const mediaRecorder = useRef&amp;lt;MediaRecorder | null&amp;gt;(null);
    const [error, setError] = useState&amp;lt;Error| null&amp;gt;(null);
    let requestId: number;
    let timer: ReturnType&amp;lt;typeof setTimeout&amp;gt;;

    const createBlob = () =&amp;gt; {
      const [chunk] = mediaChunks.current;
      const blobProperty = { type: chunk.type };
      return new Blob(mediaChunks.current, blobProperty)
    }
  ...
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code we would use mediaChunks as variable to hold input blob and mediaRecorder to have instance of new MediaRecorder which takes stream as input from &lt;code&gt;navigator.mediaDevices.getUserMedia&lt;/code&gt;. Next lets take care of cases where &lt;code&gt;getUserMedia&lt;/code&gt; is not available&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
useEffect(() =&amp;gt; {
        if(!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
            const notAvailable = new Error('Your browser does not support Audio Input')
            setError(notAvailable)
        }

    },[]);
...

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we will start writing the actual functionality of the hook which will consists of various functions like setupMediaRecorder, setupAudioContext, onRecordingStart, onRecordingActive, startRecording, stopRecording etc.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const onRecordingStart = () =&amp;gt; mediaChunks.current = [];

const onRecordingActive = useCallback(({data}: BlobEvent) =&amp;gt; {
        if(data) {
            mediaChunks.current.push(data);
            config?.onDataReceived?.(createBlob())
        }
    },[config]);

const startTimer = () =&amp;gt; {
        timer = setTimeout(() =&amp;gt; {
            stopRecording();
        }, config.timeInMillisToStopRecording)
    };

const setupMediaRecorder = ({stream}:{stream: MediaStream}) =&amp;gt; {
        mediaRecorder.current = new MediaRecorder(stream)
        mediaRecorder.current.ondataavailable = onRecordingActive
        mediaRecorder.current.onstop = onRecordingStop
        mediaRecorder.current.onstart = onRecordingStart
        mediaRecorder.current.start(config.timeSlice)

    };

 const setupAudioContext = ({stream}:{stream: MediaStream}) =&amp;gt; {
        const audioContext = new AudioContext();
        const audioStreamSource = audioContext.createMediaStreamSource(stream);
        const analyser = audioContext.createAnalyser();

        analyser.minDecibels = VOICE_MIN_DECIBELS;

        audioStreamSource.connect(analyser);
        const bufferLength = analyser.frequencyBinCount;
        const domainData = new Uint8Array(bufferLength)

        return {
            domainData,
            bufferLength,
            analyser
        }
    };

const startRecording = async () =&amp;gt; {
        setIsRecording(true);

        await navigator.mediaDevices
            .getUserMedia({
                audio: config.audio
            })
            .then((stream) =&amp;gt; {
                setupMediaRecorder({stream});
                if(config.timeSlice) {
                    const { domainData, analyser, bufferLength } = setupAudioContext({ stream });
                    startTimer()
                }
            })
            .catch(e =&amp;gt; {
                setError(e);
                setIsRecording(false)
            })
    };



    const stopRecording = () =&amp;gt; {
        mediaRecorder.current?.stop();

        clearTimeout(timer);
        window.cancelAnimationFrame(requestId);

        setIsRecording(false);
        onRecordingStop()
    };

    const createBlob = () =&amp;gt; {
        const [chunk] = mediaChunks.current;
        const blobProperty = { type: chunk.type };
        return new Blob(mediaChunks.current, blobProperty)
    }

    const onRecordingStop = () =&amp;gt; config?.onStop?.();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with the above code we are almost done with the hook, the only pending thing is to identify whether user has stopped speaking or not, we would use &lt;code&gt;DELAY_BETWEEN_DIALOGUE&lt;/code&gt; as the time that we would wait for, if there is no input for 2 secs we will assume that user has stopped speaking and will hit the speech to text endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
const detectSound = ({ 
        recording,
        analyser,
        bufferLength,
        domainData
    }: {
        recording: boolean
        analyser: AnalyserNode
        bufferLength: number
        domainData: Uint8Array
    }) =&amp;gt; {
        let lastDetectedTime = performance.now();
        let anySoundDetected = false;

        const compute = () =&amp;gt; {
            if (!recording) {
                return;
            }

            const currentTime = performance.now();

            const timeBetweenTwoDialog =
                anySoundDetected === true &amp;amp;&amp;amp; currentTime - lastDetectedTime &amp;gt; DELAY_BETWEEN_DIALOGUE;

            if (timeBetweenTwoDialog) {
                stopRecording();

                return;
            }

            analyser.getByteFrequencyData(domainData);

            for (let i = 0; i &amp;lt; bufferLength; i += 1) {
                if (domainData[i] &amp;gt; 0) {
                    anySoundDetected = true;
                    lastDetectedTime = performance.now();
                }
            }

            requestId = window.requestAnimationFrame(compute);
        };

        compute();

    }
...

const startRecording = async () =&amp;gt; {
 ... 
  detectSound()
 ... 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;in the above code we are using requestAnimationFrame to detect the user audio input, with this we are done with the hook and now can start using the hook in various places. &lt;/p&gt;

&lt;p&gt;e.g&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
  const onDataReceived = async (data: BodyInit) =&amp;gt; {
    const rawResponse = await fetch('https://backend-endpoint', {
      method: 'POST',
      body: data
    });
    const response = await rawResponse.json();

    setText(response)
  };

  const { isRecording, startRecording, error } = useAudioInput({
    audio: true,
    timeInMillisToStopRecording: 2000,
    timeSlice: 400,
    onDataReceived
  })

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second part is to wire up a node server which can communicate with google speech to text api, i have attached the documentation that i referred while creating the node side of things. &lt;br&gt;
&lt;code&gt;https://codelabs.developers.google.com/codelabs/cloud-speech-text-node.&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// demo node server which connects with google speech to text api endpoint

const express = require('express');
const cors = require('cors');

const speech = require('@google-cloud/speech');

const client = new speech.SpeechClient();

async function convert(audioBlob) {
  const request = {
    config: {
      encoding: 'WEBM_OPUS', // Ensure this matches the format of the audio being sent
      sampleRateHertz: 48000, // This should match the sample rate of your recording
      languageCode: 'en-US'
    },
    audio: {
      content: audioBlob
    }
  };

  const [response] = await client.recognize(request);

  const transcription = response.results
    .map(result =&amp;gt; result.alternatives[0].transcript)
    .join('\n');
  return transcription;
}

const app = express();

app.use(cors())
app.use(express.json());

app.post('/upload', express.raw({ type: '*/*' }), async (req, res) =&amp;gt; {
    const audioBlob = req.body;

    const response = await convert(audioBlob);

    res.json(response);
});

app.listen(4000,'0.0.0.0', () =&amp;gt; {
  console.log('Example app listening on port 4000!');
});


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;in this article i have covered sending audio content or blob to the google speech to text endpoint, we can also send an blob uri instead of content the only change will be is the payload&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// sending url as part of audio object to speech to text api 
...
audio: {url: audioUrl} or audio: {content: audioBlob}
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code related to the article is present in &lt;a href="https://github.com/shubhadip/react-audio-hook" rel="noopener noreferrer"&gt;Github&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>node</category>
      <category>speechtotext</category>
    </item>
    <item>
      <title>Getting started with React Dnd-Kit</title>
      <dc:creator>shubhadip</dc:creator>
      <pubDate>Sat, 16 Jul 2022 14:58:39 +0000</pubDate>
      <link>https://dev.to/shubhadip/getting-started-with-react-dnd-kit-3djb</link>
      <guid>https://dev.to/shubhadip/getting-started-with-react-dnd-kit-3djb</guid>
      <description>&lt;p&gt;In this article we are going to use one of the drag n drop library for react. There are few good drag n drop libraries for react like &lt;a href="https://react-dnd.github.io/react-dnd/"&gt;react-dnd&lt;/a&gt;, &lt;a href="https://dndkit.com/"&gt;dnd-kit&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/atlassian/react-beautiful-dnd"&gt;react-beautiful-dnd&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We are going to look into dnd-kit today reason behind using this library was that it supports a lot of usecase, availability of hooks, being the light-weight etc.&lt;/p&gt;

&lt;p&gt;To start with lets create an react app with create-react-app &amp;amp; install the necessary libraries with it &lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx create-react-app react-dndkit-eg&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install --save @dnd-kit/core @dnd-kit/sortable&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;@dntkit/core &amp;amp; @dndkit/sortable these are the two libraries which we will be requiring to support basic dragndrop functionalities, library also provides support various other features with other libs like &lt;code&gt;@dnd-kit/utilities&lt;/code&gt;, &lt;code&gt;@dnd-kit/modifiers&lt;/code&gt;, &lt;code&gt;@dnd-kit/accessibility&lt;/code&gt; more details about each of these can be read on their website.&lt;/p&gt;

&lt;p&gt;To start with we will be creating component which will be wrapping our draggable/sortable component, the whole idea of dnd in simple terms it to have a container where your item can be dragged into or moved across so the code regarding the component will somewhat look like this &lt;a href="https://github.com/shubhadip/react-dndkit-eg/blob/master/src/components/sortableItem.js"&gt;sortable-component-complete-code&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
# e.g code snippet
export function SortableItem(props) {
  const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition,
  } = useSortable({id: props.id}); 

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
  };

  if(props.handle) {
    return (&amp;lt;div ref={setNodeRef} style={style} {...attributes} className={'pos-relative'}&amp;gt;
      {
        props.handle ? 
        &amp;lt;span className='dragHandleClass' {...listeners}&amp;gt;
          # some svg/img/htmlelement
        &amp;lt;/span&amp;gt;
        : null
      }
      {props.children}
    &amp;lt;/div&amp;gt;)
  }

  return (
    &amp;lt;div ref={setNodeRef} style={style} {...attributes}   {...listeners} className={'pos-relative'}&amp;gt;
      {props.children}
    &amp;lt;/div&amp;gt;
  );
}
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the above code acts as wrapper for the component that needs to dragged/sorted, we will talk about the handle prop later in the article, the &lt;code&gt;id&lt;/code&gt; prop is passed to useSortable hook so that every item can be uniquely identified.&lt;/p&gt;

&lt;p&gt;Now lets create a component which will be having multiple items that can be sorted or dragged on, to create a container we would need DndContext &amp;amp; SortableContext so that the grid &amp;amp; row elements can be moved/sorted across.&lt;/p&gt;

&lt;p&gt;DndContext takes a fews props some of them are sensors, collisionDetection etc these also includes functions like handleDragStart &amp;amp; handleDragEnd these are the functions which can be use before &amp;amp; after the whole dragndrop interaction. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;Similarly SortableContext takes few props , here we will have to pass items which should be an array on uniqueIds &amp;amp; this should be same as that we have passed to sortableItem above.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The code of the context should be similar to this &lt;a href="https://github.com/shubhadip/react-dndkit-eg/blob/master/src/components/gridEg/index.js"&gt;sortable-context-complete-code&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
# e.g sortable context/container

&amp;lt;DndContext 
          id={'grid-dnd-basic'}
          onDragEnd={handleDragEnd}
          sensors={sensors}
          collisionDetection={closestCenter}
        &amp;gt;
            &amp;lt;SortableContext 
              id={'grid-sort-contextbasic'}
              items={items.map((i) =&amp;gt; i?.id)}
            &amp;gt;
              {items.map(value =&amp;gt; {

                return (
                  &amp;lt;SortableItem handle={true} key={value?.id} id={value?.id} &amp;gt;
# sortableItem Content
                  &amp;lt;/SortableItem&amp;gt;
                )
              })}
            &amp;lt;/SortableContext&amp;gt;
          &amp;lt;/DndContext&amp;gt;
...

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point we are done with our component setup regarding drag n drop, now we will have to add handler to functions like handleDragStart/handleDragEnd, code for these are almost similar to what the documentation of dnd-kit provides only change is the items property that is passed to the handler function&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
  function handleDragEnd(event) {
    const {active, over} = event;

    if (active.id !== over.id) {
      setItems((items) =&amp;gt; {
        const oldIndex = (items.map(i =&amp;gt; i.id)).indexOf(active.id);
        const newIndex = (items.map(i =&amp;gt; i.id)).indexOf(over.id);

        return arrayMove(items, oldIndex, newIndex);
      });
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;in the above code you can see we are using map function to pass only ids for the indexOf function as everything is mapped to uniqueId that is passed to sortableItem &amp;amp; sortableContext.&lt;br&gt;
So we are almost ready with our dnd-kit implementation for sorting using dndContext &amp;amp; sortableContext. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;Now lets visit the handle prop that we have used earlier in our sortableItem, so we can see useSortable provides a listeners now if we want to drag the item using some handler and not the actual item then we can use handle prop to apply listener to the drag-handler directly, in this way we will be able to drag via some handler and not he actual item&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# e.g handle implementation
...
  if(props.handle) {
    return (&amp;lt;div ref={setNodeRef} style={style} {...attributes} className={'pos-relative'}&amp;gt;
      {
        props.handle ? 
        &amp;lt;span className='dragHandleClass' {...listeners}&amp;gt;
          # svg/image/html-element
        &amp;lt;/span&amp;gt;
        : null
      }
      {props.children}
    &amp;lt;/div&amp;gt;)
  }
  ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now lets talk about the &lt;a href="https://docs.dndkit.com/api-documentation/sensors#built-in-sensors"&gt;sensors&lt;/a&gt; in the whole e.g we will be using the basic sensor implementation in the doc  which looks similar to this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const sensors = useSensors(
    useSensor(PointerSensor),
    useSensor(KeyboardSensor, {
      coordinateGetter: sortableKeyboardCoordinates,
    })
  );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now these use sensor takes second parameters as object which also has a property &lt;code&gt;activationConstraint&lt;/code&gt;, now this can be used to activate the sensor only after some pixel movements &lt;a href="https://github.com/shubhadip/react-dndkit-eg/blob/master/src/components/gridSwapEg/index.js#L29"&gt;code&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const sensors = useSensors(
    useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
    useSensor(KeyboardSensor, {
      coordinateGetter: sortableKeyboardCoordinates,
    })
  );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;The scenarios where this can be used is when you have an click listener to you sortableItem &amp;amp; you don't use drag-handle, so that we can uniquely identify click &amp;amp; drag events, without this constraint event click will be treated as drag events &amp;amp; our drag-handler functions like onDragEnd &amp;amp; onDragStart will be triggered.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;this article mostly covers topics related to &lt;a href="https://docs.dndkit.com/api-documentation/context-provider"&gt;dndContext&lt;/a&gt; and &lt;a href="https://docs.dndkit.com/presets/sortable"&gt;sortable&lt;/a&gt; preset more about the same can be read at dnd-kit website.&lt;/p&gt;

&lt;p&gt;The code related to the article is present in the &lt;a href="https://github.com/shubhadip/react-dndkit-eg"&gt;GithubLink&lt;/a&gt; &amp;amp; &lt;a href="https://shubhadip.github.io/react-dndkit-eg/"&gt;gh-pages&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dndkit</category>
      <category>react</category>
      <category>dragndrop</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Adding server-side-rendering to existing vue 3 project</title>
      <dc:creator>shubhadip</dc:creator>
      <pubDate>Sun, 11 Jul 2021 12:45:58 +0000</pubDate>
      <link>https://dev.to/shubhadip/adding-server-side-rendering-to-existing-vue-3-project-53oo</link>
      <guid>https://dev.to/shubhadip/adding-server-side-rendering-to-existing-vue-3-project-53oo</guid>
      <description>&lt;p&gt;In this article we will see how to add server side rendering support to existing vue 3 project.I will be using one of my existing vue3 &amp;amp; vuex project which is available in &lt;a href="https://github.com/shubhadip/vuex-typescript"&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First we have to add few dependencies &amp;amp;&amp;amp; devdependencies so that our project can support ssr&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add @vue/server-renderer vue@3.1.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add -D webpack-manifest-plugin webpack-node-externals express
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;NOTE: upgrading vue to latest version so that we can use onServerPrefetch lifecycle hook&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;for server-side-rendering we will have to create two different entry points(files) one, which will be used in server &amp;amp; another in client side also we will need to different build commands for server/client, lets add these two first in package.json scripts section&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"build:client": "vue-cli-service build --dest dist/client",
"build:server": "VUE_APP_SSR=true vue-cli-service build --dest dist/server",
"build:ssr": "rm -rf ./dist &amp;amp;&amp;amp; npm run build:client &amp;amp;&amp;amp; npm run build:server"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we have added a flag &lt;code&gt;VUE_APP_SSR=true&lt;/code&gt; which would help us for bundling server side and ignore any window logics as those won't work in server-side.There will be two separate directory within dist folder client &amp;amp;&amp;amp; server having separate code.&lt;/p&gt;

&lt;p&gt;With build scripts ready lets move to entry files of server side &amp;amp; client side, we will have a common &lt;code&gt;main.ts&lt;/code&gt; file which will be included in both entry files &lt;code&gt;entry-client.ts&lt;/code&gt; &amp;amp;&amp;amp; &lt;code&gt;entry-server.ts&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Lets create main.ts, we have to take care of createApp &amp;amp;&amp;amp; createSSRApp for respective entry points.we can make use of flag &lt;code&gt;VUE_APP_SSR=true&lt;/code&gt; or &lt;code&gt;typeof window&lt;/code&gt; check&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const isSSR = typeof window === 'undefined';
const app = (isSSR ? createSSRApp : createApp)(rootComponent)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the end our file would look something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createSSRApp, createApp, h } from 'vue'
import App from './App.vue'
import router from './router';
import { store } from './store'

export default function () {
  const isSSR = typeof window === 'undefined';
  const rootComponent = {
    render: () =&amp;gt; h(App),
    components: { App },
  }
const app = (isSSR ? createSSRApp : createApp)(rootComponent)
  app.use(router);
  app.use(store);
  return {
    app,
    router,
    store
  };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the main crux ready lets create entry-client.ts &amp;amp;&amp;amp; entry-server.ts&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# entry-server.ts
import createApp from './main';

export default function () {

  const {
    router,
    app,
    store
  } = createApp();

  return {
    app,
    router,
    store
  };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In server entry file, we are just exporting app,router,store which would be used while serving via express&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# entry-client.ts
import createApp from './main'
declare let window: any;

const { app, router, store } = createApp();

(async (r, a, s) =&amp;gt; {
  const storeInitialState = window.INITIAL_DATA;

  await r.isReady();

  if (storeInitialState) {
    s.replaceState(storeInitialState);
  }

  a.mount('#app', true);
})(router, app, store);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;window.INITIAL_DATA will hold the initialData that would be prefetched in server-side and would be stored in global window object, then in clientSide we will use this data to populate our store on first load.&lt;/p&gt;

&lt;p&gt;Now,lets move to webpack config part of SSR, to work with webpack we have to create a vue.config.js file. we would include webpack-manifest-plugin,webpack-node-externals,webpack&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const ManifestPlugin = require("webpack-manifest-plugin");
const nodeExternals = require("webpack-node-externals");
const webpack = require('webpack');
const path = require('path');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets add config, i will be using export.chainWebpack directly to modify default webpack config provided by vue&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;exports.chainWebpack = webpackConfig =&amp;gt; {
   if (!process.env.VUE_APP_SSR) {
    webpackConfig
      .entry("app")
      .clear()
      .add("./src/entry-client.ts");
    return;
  }

  webpackConfig
    .entry("app")
    .clear()
    .add("./src/entry-server.ts");

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;based on which build is going to run we have added different entry points, for this we will use &lt;code&gt;VUE_APP_SSR&lt;/code&gt; flag.&lt;/p&gt;

&lt;p&gt;Now we have to add few more code so that webpack can build server-side bundle properly.we have to set target to node &amp;amp;&amp;amp; libraryFormat to commonjs2 since this file is going to run via express&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  webpackConfig.target("node");
  webpackConfig.output.libraryTarget("commonjs2");

  webpackConfig
    .plugin("manifest")
    .use(new ManifestPlugin({ fileName: "ssr-manifest.json" }));

  webpackConfig.externals(nodeExternals({ allowlist: [/\.(css|vue)$/,] 
  }));
  webpackConfig.optimization.splitChunks(false).minimize(false);

  webpackConfig.plugins.delete("hmr");
  webpackConfig.plugins.delete("preload");
  webpackConfig.plugins.delete("prefetch");
  webpackConfig.plugins.delete("progress");
  webpackConfig.plugins.delete("friendly-errors");
  webpackConfig.plugin('limit').use(
    new webpack.optimize.LimitChunkCountPlugin({
      maxChunks: 1
    })
  )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you can read more about this configuration on this &lt;a href="https://v3.vuejs.org/guide/ssr/build-config.html#example-configuration"&gt;SSRbuildConfig&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;the last part is to create an server.js file which we will run on server via express.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const path = require('path');
const fs = require('fs');
const serialize = require('serialize-javascript');
const express = require('express');
const { renderToString } = require("@vue/server-renderer");
const  PORT = process.env.PORT || 4455
const manifest = require("../dist/server/ssr-manifest.json");
const appPath = path.join(__dirname, "../dist",'server', manifest["app.js"]);
const App = require(appPath).default;

const server = express();

server.use("/img", express.static(path.join(__dirname, "../dist/client", "img")));
server.use("/js", express.static(path.join(__dirname, "../dist/client", "js")));
server.use("/manifest.json", express.static(path.join(__dirname, "../dist/client", "manifest.json")));
server.use("/css", express.static(path.join(__dirname, "../dist/client", "css")));
server.use(
  "/favicon.ico",
  express.static(path.join(__dirname, "../dist/client", "favicon.ico"))
);

server.get('*', async (req, res) =&amp;gt; {
  const { app, router, store } = await App(req);

  await router.push(req.url);
  await router.isReady();

  let appContent = await renderToString(app);

  const renderState = `
    &amp;lt;script&amp;gt;
      window.INITIAL_DATA = ${serialize(store.state)}
    &amp;lt;/script&amp;gt;`;

  fs.readFile(path.join(__dirname, '../dist/client/index.html'), (err, html) =&amp;gt; {
    if (err) {
      throw err;
    }

    appContent = `&amp;lt;div id="app"&amp;gt;${appContent}&amp;lt;/div&amp;gt;`;

    html = html.toString().replace('&amp;lt;div id="app"&amp;gt;&amp;lt;/div&amp;gt;', `${renderState}${appContent}`);
    res.setHeader('Content-Type', 'text/html');
    res.send(html);
  });
});

server.listen(PORT, ()=&amp;gt;{
  console.log(`server listening at port ${PORT}`)
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we will be using above code which will intercept all request to our server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const manifest = require("../dist/server/ssr-manifest.json");
const appPath = path.join(__dirname, "../dist",'server', manifest["app.js"]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#ssr-manifest.json
  "app.css": "/css/app.aaa5a7e8.css",
  "app.js": "/js/app.b8f9c779.js",
  "app.css.map": "/css/app.aaa5a7e8.css.map",
  "app.js.map": "/js/app.b8f9c779.js.map",
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;this is where we use manifest.json file to select appropriate  server file that would be served from express, contents of this json file is an object which has mapping for specific bundles&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;await router.push(req.url);
await router.isReady();
let appContent = await renderToString(app);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;above mentioned code will be used to match url-page properly with router.push, then renderToString will output everything as string which would be served from express.&lt;/p&gt;

&lt;p&gt;In the above &lt;code&gt;server.js&lt;/code&gt; you can see &lt;code&gt;html&lt;/code&gt; variable holds the entire content that will be served from express to browser, next step would be to add support for meta-tags.&lt;/p&gt;

&lt;p&gt;After all these configuration, now our pages can be rendered from server, now we will use axios to fetch data from endpoint which can rendered from server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# vue file
    const fetchInitialData = async () =&amp;gt; {
      const response = await axios('https://jsonplaceholder.typicode.com/posts')
      store.dispatch(AllActionTypes.USER_LISTS, response.data || [])
    }

    onServerPrefetch(async () =&amp;gt; {
     await fetchInitialData()
    })

    const listData = computed(() =&amp;gt; {
      return store.getters.getUserList || []
    });

    onMounted(async () =&amp;gt; {
      if(!listData.value.length){
        await fetchInitialData();
      }
    })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code is an example of how can we fetch data for server-side rendering, we have used &lt;code&gt;onServerPrefetch&lt;/code&gt; lifecycle method to fetch data &amp;amp;&amp;amp; for client side we are using onMounted hook incase data is not available in window from server.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: I have skipped few steps while explaining, all code regarding this article is present at &lt;a href="https://github.com/shubhadip/vuex-typescript"&gt;Repository&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Resources which helped me to create this article are &lt;br&gt;
&lt;a href="https://v3.vuejs.org/guide/ssr/introduction.html#what-is-server-side-rendering-ssr"&gt;https://v3.vuejs.org/guide/ssr/introduction.html#what-is-server-side-rendering-ssr&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=XJfaAkvLXyU&amp;amp;t=1s"&gt;youtube&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vue</category>
      <category>typescript</category>
      <category>serversiderendering</category>
    </item>
    <item>
      <title>Rewriting vue prism component in vue 3</title>
      <dc:creator>shubhadip</dc:creator>
      <pubDate>Thu, 12 Nov 2020 16:03:50 +0000</pubDate>
      <link>https://dev.to/shubhadip/rewriting-vue-prism-component-in-vue-3-epl</link>
      <guid>https://dev.to/shubhadip/rewriting-vue-prism-component-in-vue-3-epl</guid>
      <description>&lt;p&gt;With Vue 3 released, there will be many libraries running into port their vue 2 projects into vue 3. Recently while working on creating a library on vue 3 I needed a syntax highlighter for demo purpose, so thought of writing one with vue 3 Setup API.&lt;/p&gt;

&lt;p&gt;for this to work we need prismjs so let's add prismJs library;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn add prismjs&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We would be requiring code that is supposed to be used as a highlighter and the language in which the code would be.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as Vue from 'vue';
import Prism from 'prismjs';

export default Vue.defineComponent({
  props: {
    code: {
      type: String,
    },
    inline: {
      type: Boolean,
      default: false,
    },
    language: {
      type: String,
      default: 'markup',
    },
  }
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's see how can we used the setup function to access props and children. Setup function provides props and setupContext as parameters, we can easily destructure setupContext to access attrs and slots.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
setup(props, { slots, attrs }: { slots: Slots; attrs: Data }) {
    const { h } = Vue;
    const slotsData = (slots &amp;amp;&amp;amp; slots.default &amp;amp;&amp;amp; slots.default()) || [];
    const code = props.code || (slotsData.length &amp;gt; 0 ? slotsData[0].children : '');
    const { inline, language } = props;
    const prismLanguage = Prism.languages[language];
    const className = `language-${language}`;

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code will be access to props and children's passed to the prismJs.Also, &lt;code&gt;h&lt;/code&gt; which was passed to render function but now it has to be imported from vue.&lt;/p&gt;

&lt;p&gt;With this done let's see how can we pass &lt;code&gt;{{code}}&lt;/code&gt; as well as &lt;code&gt;language&lt;/code&gt; to prismJs so that it can return HTML back to us, that can be used in the render function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    const d = Prism.highlight(code, prismLanguage);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with everything in place, lets add our render function with these data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
return (): VNode =&amp;gt;
      h('pre', { ...attrs, class: [attrs.class, className] }, [
        h('code', {
          class: className,
          innerHTML: d,
        }),
      ]);
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In 3.x, the entire VNode props structure is flattened. you can read more about render function &lt;a href="https://v3.vuejs.org/guide/migration/render-function-api.html#_2-x-syntax-3"&gt;Vue 3 Render Function&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So this is how our code will look on completion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// prismcomponent/index.ts
import * as Vue from 'vue';
import Prism from 'prismjs';
import { Slots, VNode } from 'vue';

declare type Data = Record&amp;lt;string, unknown&amp;gt;;

export default Vue.defineComponent({
  props: {
    code: {
      type: String,
    },
    inline: {
      type: Boolean,
      default: false,
    },
    language: {
      type: String,
      default: 'markup',
    },
  },
  setup(props, { slots, attrs }: { slots: Slots; attrs: Data }) {
    const { h } = Vue;
    const slotsData = (slots &amp;amp;&amp;amp; slots.default &amp;amp;&amp;amp; slots.default()) || [];
    const code = props.code || (slotsData.length &amp;gt; 0 ? slotsData[0].children : '');
    const { inline, language } = props;
    const prismLanguage = Prism.languages[language];
    const className = `language-${language}`;

    if (inline) {
      return (): VNode =&amp;gt;
        h('code', { ...attrs, class: [attrs.class, className], innerHTML: Prism.highlight(code, prismLanguage) });
    }

    const d = Prism.highlight(code, prismLanguage);
    return (): VNode =&amp;gt;
      h('pre', { ...attrs, class: [attrs.class, className] }, [
        h('code', {
          class: className,
          innerHTML: d,
        }),
      ]);
  },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So in other components, all we need to add is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;template&amp;gt;
...
&amp;lt;Prism language="javascript" class="codesnippet"&amp;gt;
  {{ code }}
&amp;lt;/Prism&amp;gt;
...
&amp;lt;/template&amp;gt;

&amp;lt;script lang="ts"&amp;gt;
import { defineComponent } from 'vue';
import Prism from '../prismcomponent';
import 'prismjs';
import 'prismjs/themes/prism.css';

export default defineComponent({
...
setup() {
    const code = `const c = a+b`;
    return {
      code,
    };
  },
...
})
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>vue3</category>
      <category>vue</category>
      <category>prismjs</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Vue 3, Vuex 4 Modules, Typescript</title>
      <dc:creator>shubhadip</dc:creator>
      <pubDate>Thu, 29 Oct 2020 05:48:14 +0000</pubDate>
      <link>https://dev.to/shubhadip/vue-3-vuex-4-modules-typescript-2i2o</link>
      <guid>https://dev.to/shubhadip/vue-3-vuex-4-modules-typescript-2i2o</guid>
      <description>&lt;p&gt;In this article we will going to see how can we use &lt;code&gt;typescript&lt;/code&gt; along with &lt;code&gt;Vue 3&lt;/code&gt; and &lt;code&gt;Vuex 4&lt;/code&gt; and &lt;code&gt;Vuex Modules&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Vuex 4 is still in beta version. Also along with removal of global &lt;code&gt;this&lt;/code&gt; from vue, &lt;code&gt;this.$store&lt;/code&gt; is also removed from vuex 4 and its upto developer to write typings for store.You can read more about from &lt;a href="https://github.com/vuejs/vuex/releases/tag/v4.0.0-beta.1" rel="noopener noreferrer"&gt;vuex-release-note&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We will be creating modules like root and counter wherein counter will be module and root as RootState.Lets create a module folder inside store folder and an index.ts which will export our store.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fukpau7do17obv3jv7xrw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fukpau7do17obv3jv7xrw.png" alt="Store Structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;we will also have interfaces.ts file which will have all interfaces for our store.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { ActionContext } from "vuex";
import { MutationTypes as CounterMTypes } from "./modules/counter/mutation-types";
import { ActionTypes as CounterATypes } from "./modules/counter/action-types";

export interface IRootState {
  root: boolean;
  version: string;
}

export interface CounterStateTypes {
  counter?: number;
  rootDispatch?: boolean
}

export interface CounterGettersTypes {
  doubledCounter(state: CounterStateTypes): number;
  counterValue(state: CounterStateTypes): number;
}

export type CounterMutationsTypes&amp;lt;S = CounterStateTypes&amp;gt; = {
  [CounterMTypes.SET_COUNTER](state: S, payload: number): void;
  [CounterMTypes.RESET_COUNTER](state: S): void;
};

export type AugmentedActionContext = {
  commit&amp;lt;K extends keyof CounterMutationsTypes&amp;gt;(
    key: K,
    payload: Parameters&amp;lt;CounterMutationsTypes[K]&amp;gt;[1]
  ): ReturnType&amp;lt;CounterMutationsTypes[K]&amp;gt;;
} &amp;amp; Omit&amp;lt;ActionContext&amp;lt;CounterStateTypes, IRootState&amp;gt;, "commit"&amp;gt;;

export interface CounterActionsTypes {
  [CounterATypes.GET_COUNTER](
    { commit }: AugmentedActionContext,
    payload: number
  ): void;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;so CounterActionsTypes acts as interface for actions in counter module, CounterMutationsTypes acts as interface for mutations in counter module and CounterGettersTypes acts as interface for getters.You can read more about utility like Omit etc from &lt;a href="https://www.typescriptlang.org/docs/handbook/utility-types.html" rel="noopener noreferrer"&gt;typescript-utility&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lets start by creating counter module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// store/modules/counter/state.ts
import { CounterStateTypes } from "./../../interfaces";

export const state: CounterStateTypes = {
  counter: 0,
};

// store/modules/counter/action-types.ts
export enum ActionTypes {
  GET_COUNTER = "GET_COUNTER"
}

// store/modules/counter/mutation-types.ts
export enum MutationTypes {
  SET_COUNTER = "SET_COUNTER",
  RESET_COUNTER = "RESET_COUNTER"
}

// store/modules/counter/getters.ts

import { GetterTree } from "vuex";
import {
  CounterGettersTypes,
  CounterStateTypes,
  IRootState
} from "./../../interfaces";

export const getters: GetterTree&amp;lt;CounterStateTypes, IRootState&amp;gt; &amp;amp;
  CounterGettersTypes = {
  counterValue: (state: CounterStateTypes) =&amp;gt; {
    return state.counter || 0;
  },
  doubledCounter: (state: CounterStateTypes) =&amp;gt; {
    return state.counter || 0 * 2;
  }
};

// store/modules/counter/mutations.ts
import { MutationTree } from "vuex";
import { MutationTypes } from "./mutation-types";
import { CounterMutationsTypes, CounterStateTypes } from "./../../interfaces";

export const mutations: MutationTree&amp;lt;CounterStateTypes&amp;gt; &amp;amp;
  CounterMutationsTypes = {
  [MutationTypes.SET_COUNTER](state: CounterStateTypes, payload: number) {
    state.counter = payload;
  },
  [MutationTypes.RESET_COUNTER](state: CounterStateTypes) {
    state.counter = 0;
  }
};

// store/modules/counter/actions.ts
import { ActionTree } from "vuex";
import { ActionTypes } from "./action-types";
import { MutationTypes } from "./mutation-types";
import {
  CounterActionsTypes,
  CounterStateTypes,
  IRootState
} from "@/store/interfaces";

export const actions: ActionTree&amp;lt;CounterStateTypes, IRootState&amp;gt; &amp;amp;
  CounterActionsTypes = {
  [ActionTypes.GET_COUNTER]({ commit }, payload: number) {
    commit(MutationTypes.SET_COUNTER, payload);
  }
};

// store/modules/counter/index.ts
import { Module } from "vuex";
import { CounterStateTypes, IRootState } from "@/store/interfaces";
import { getters } from "./getters";
import { actions } from "./actions";
import { mutations } from "./mutations";
import { state } from "./state";

// Module
const counter: Module&amp;lt;CounterStateTypes, IRootState&amp;gt; = {
  state,
  getters,
  mutations,
  actions
};

export default counter;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have created module counter we have to add types, lets create types.ts in counter module&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// store/modules/counter/types.ts
import {
  CounterStateTypes,
  CounterMutationsTypes,
  CounterGettersTypes,
  CounterActionsTypes
} from "@/store/interfaces";
import { Store as VuexStore, CommitOptions, DispatchOptions } from "vuex";

export type CounterStoreModuleTypes&amp;lt;S = CounterStateTypes&amp;gt; = Omit&amp;lt;
  VuexStore&amp;lt;S&amp;gt;,
  "commit" | "getters" | "dispatch"
&amp;gt; &amp;amp; {
  commit&amp;lt;
    K extends keyof CounterMutationsTypes,
    P extends Parameters&amp;lt;CounterMutationsTypes[K]&amp;gt;[1]
  &amp;gt;(
    key: K,
    payload?: P,
    options?: CommitOptions
  ): ReturnType&amp;lt;CounterMutationsTypes[K]&amp;gt;;
} &amp;amp; {
  getters: {
    [K in keyof CounterGettersTypes]: ReturnType&amp;lt;CounterGettersTypes[K]&amp;gt;;
  };
} &amp;amp; {
  dispatch&amp;lt;K extends keyof CounterActionsTypes&amp;gt;(
    key: K,
    payload?: Parameters&amp;lt;CounterActionsTypes[K]&amp;gt;[1],
    options?: DispatchOptions
  ): ReturnType&amp;lt;CounterActionsTypes[K]&amp;gt;;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So this creates our counter module along with its types.Lets now focus on root, we will be creating a root folder inside modules which will be used as root for vuex store.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhh4qxx3hiivnmv3tdcfh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhh4qxx3hiivnmv3tdcfh.png" alt="store structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is how our folder structure will look like after this.&lt;br&gt;
only extra thing that we have to do in root module is to add modules to it rest all is similar to counter module&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// store/modules/root/index.ts

import { Module, ModuleTree } from "vuex";
import { IRootState } from "@/store/interfaces";
import { getters } from "./getters";
import { actions } from "./actions";
import { mutations } from "./mutations";
import { state } from "./state";
import counterModule from "../counter";

// Modules
const modules: ModuleTree&amp;lt;IRootState&amp;gt; = {
  counterModule,
};

const root: Module&amp;lt;IRootState, IRootState&amp;gt; = {
  state,
  getters,
  mutations,
  actions,
  modules
};

export default root;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you can see we have added modules to root and we can pass this to createStore.&lt;/p&gt;

&lt;p&gt;so with this done now we can set up index.ts in our store folder&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createStore } from "vuex";
import { IRootState } from "@/store/interfaces";
import { CounterStoreModuleTypes } from "./modules/counter/types";
import { RootStoreModuleTypes } from "./modules/root/types";

import root from "./modules/root";

export const store = createStore&amp;lt;IRootState&amp;gt;(root);

type StoreModules = {
  counter: CounterStoreModuleTypes;
  root: RootStoreModuleTypes;
};

export type Store = CounterStoreModuleTypes&amp;lt;Pick&amp;lt;StoreModules, "counter"&amp;gt;&amp;gt; &amp;amp;
  Counter1StoreModuleTypes&amp;lt;Pick&amp;lt;StoreModules, "counter1"&amp;gt;&amp;gt; &amp;amp;
  RootStoreModuleTypes&amp;lt;Pick&amp;lt;StoreModules, "root"&amp;gt;&amp;gt;;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;createStore&amp;lt;IRootState&amp;gt;(root);&lt;/code&gt; with this we are marking root module as rootState.&lt;code&gt;Store&lt;/code&gt; will act as type for our whole store.&lt;/p&gt;

&lt;p&gt;I have also created an action-types.ts and mutation-types.ts inside store folder so that we can have all actions and mutation at one place.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// store/action-types.ts
import { ActionTypes as counterTypes } from "./modules/counter/action-types";
import { ActionTypes as rootATypes } from "./modules/root/action-types";

export const AllActionTypes = { ...counterTypes, ...rootATypes };

// store/mutation-types.ts
import { MutationTypes as counterTypes } from "./modules/counter/mutation-types";
import { MutationTypes as rootMTypes } from "./modules/root/mutation-types";

export const AllMutationTypes = {...counterTypes,...rootMTypes };

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: since i am spreading types, in case of same action/mutation types might override. we can prevent this by using namespace or completely avoiding this.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This completes our store,lets see how we can use our store in components.&lt;br&gt;
we will be creating a useStore utility in src/use folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Store } from "@/store";

import { useStore as VuexStore } from "vuex";
/**
 * Returns Whole Store Object
 */
export function useStore(): Store {
  return VuexStore() as Store;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can import useStore directly in our views and components.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuh0fetnp9hbyxr9km15l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuh0fetnp9hbyxr9km15l.png" alt="type support"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frdvalqnv9o8z1oi9r34f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frdvalqnv9o8z1oi9r34f.png" alt="type support"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this way we can have type support for store as well as we added modules in store along with types.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note : I have skipped few steps of creating store, all code regarding this article is present at &lt;a href="https://github.com/shubhadip/vuex-typescript" rel="noopener noreferrer"&gt;Repository&lt;/a&gt;.&lt;br&gt;
Articles which i went through before being able to get modules working &lt;a href="https://dev.to/3vilarthas/vuex-typescript-m4j"&gt;vuex-typescript&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>vue3</category>
      <category>vue</category>
      <category>typescript</category>
      <category>vuex</category>
    </item>
    <item>
      <title>Component Library( Vue 3  + Rollup)</title>
      <dc:creator>shubhadip</dc:creator>
      <pubDate>Fri, 23 Oct 2020 10:34:49 +0000</pubDate>
      <link>https://dev.to/shubhadip/vue-3-component-library-270p</link>
      <guid>https://dev.to/shubhadip/vue-3-component-library-270p</guid>
      <description>&lt;h1&gt;
  
  
  This is part2 of creating a component library using vue 3 and rollup.we will be building a rollup configuration so that we can build our library.
&lt;/h1&gt;

&lt;p&gt;Lets install few rollup libraries that we require&lt;br&gt;
&lt;code&gt;yarn add @babel/preset-env@7.12.1 @rollup/plugin-alias@3.1.1 @rollup/plugin-babel@5.2.1 @rollup/plugin-commonjs@14.0.0 @rollup/plugin-image@2.0.5 @rollup/plugin-node-resolve@9.0.0 @rollup/plugin-replace@2.3.3 @rollup/plugin-url@5.0.1 &lt;br&gt;
rollup@2.30.0 rollup-plugin-postcss@3.1.8 rollup-plugin-terser@7.0.2 rollup-plugin-vue@6.0.0-beta.10 rimraf@3.0.2 cross-env@7.0.2&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;lets update our babel.config.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {
  presets: [
    "@babel/preset-env"
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;rollup-plugin-vue will be used to process vue templates and rollup-plugin-postcss with handle our postcss.&lt;/p&gt;

&lt;p&gt;Now that we have all our dependencies we can write our config.Lets create a rollup.config.js, first we will create a baseconfig which can be reused for different module system builds&lt;/p&gt;

&lt;p&gt;Lets import all dependencies that we require&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import fs from 'fs';
import path from 'path';
import vue from 'rollup-plugin-vue';
import alias from '@rollup/plugin-alias';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import babel from '@rollup/plugin-babel';
import PostCSS from 'rollup-plugin-postcss';
import simplevars from 'postcss-simple-vars'
import postcssImport from 'postcss-import'
import minimist from 'minimist';
import postcssUrl from 'postcss-url'
import url from '@rollup/plugin-url'
import nested from 'postcss-nested'
import { terser } from 'rollup-plugin-terser'
import  autoprefixer  from 'autoprefixer

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;lets add a variable which we can use to identify which module are we going to build and our project root path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const argv = minimist(process.argv.slice(2));
const projectRoot = path.resolve(__dirname, '.');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we will be adding a script like this in package.json&lt;br&gt;
&lt;code&gt;"build:es": "rimraf dist &amp;amp;&amp;amp; cross-env NODE_ENV=production rollup --config rollup.config.js --format es"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Lets create baseConfig now, baseconfig will have configuration realted to vue, it will deal with preVue, Vue, postVue and babelConfig.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const baseConfig = {
  plugins: {
    preVue: [
      alias({
        entries: [
          {
            find: '@',
            replacement: `${path.resolve(projectRoot, 'src')}`,
          },
        ],
        customResolver: resolve({
          extensions: ['.js', '.jsx', '.vue'],
        }),
      }),
    ],
    replace: {
      'process.env.NODE_ENV': JSON.stringify('production'),
      __VUE_OPTIONS_API__: JSON.stringify(true),
      __VUE_PROD_DEVTOOLS__: JSON.stringify(false),
    },
    vue: {
      target: 'browser',
      preprocessStyles: true,
      postcssPlugins:[
       ...postcssConfigList
      ]
    },
    postVue: [
      // Process only `&amp;lt;style module&amp;gt;` blocks.
      PostCSS({
        modules: {
          generateScopedName: '[local]___[hash:base64:5]',
        },
        include: /&amp;amp;module=.*\.css$/,
      }),
      // Process all `&amp;lt;style&amp;gt;` blocks except `&amp;lt;style module&amp;gt;`.
      PostCSS({ include: /(?&amp;lt;!&amp;amp;module=.*)\.css$/,
        plugins:[
          ...postcssConfigList
        ]
       }),
        url({
          include: [
            '**/*.svg',
            '**/*.png',
            '**/*.gif',
            '**/*.jpg',
            '**/*.jpeg'
          ]
        }),
    ],
    babel: {
      exclude: 'node_modules/**',
      extensions: ['.js', '.jsx', '.vue'],
      babelHelpers: 'bundled',
    },
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;above config will be used to different builds we also have postconfig which is used in different places.&lt;/p&gt;

&lt;p&gt;baseConfig.vue is the part which is utilized by rollup-plugin-vue to compiler our codebase and then different plugins act accordingly.&lt;/p&gt;

&lt;p&gt;Before we proceed further we will declare some globals and externals which is used by rollup to identify external dependency and global outputs.&lt;br&gt;
&lt;code&gt;const external = ['vue'];&lt;/code&gt;&lt;br&gt;
&lt;code&gt;const globals = {  vue: 'Vue' };&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Lets create a entry points for our projects, there will be one default entry point as src/index.js and different with each components index.js e.g components/helloworld/index.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const baseFolder = './src/'
const componentsFolder = 'components/'

const components = fs
  .readdirSync(baseFolder + componentsFolder)
  .filter((f) =&amp;gt;
    fs.statSync(path.join(baseFolder + componentsFolder, f)).isDirectory()
  )

const entriespath = {
  index: './src/index.js',
  ...components.reduce((obj, name) =&amp;gt; {
    obj[name] = (baseFolder + componentsFolder + name + '/index.js')
    return obj
  }, {})
}

const capitalize = (s) =&amp;gt; {
  if (typeof s !== 'string') return ''
  return s.charAt(0).toUpperCase() + s.slice(1)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that entry points are ready lets write the crux of the module bundlers.We will be using above mentioned &lt;code&gt;argv&lt;/code&gt; to identify which module build are we supposed to build.&lt;/p&gt;

&lt;p&gt;Lets start with esm build &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;(argv === 'es')&lt;br&gt;
configuration will be as follows :&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Customize configs for individual targets
let buildFormats = [];
// this will hold our whole configuration object 

if (!argv.format || argv.format === 'es') {
  const esConfig = {
    input: entriespath,
    external,
    output: {
        format: 'esm',
        dir: 'dist/esm'
    },
    plugins: [
      commonjs(),
      replace(baseConfig.plugins.replace),
      ...baseConfig.plugins.preVue,
      vue(baseConfig.plugins.vue),
      ...baseConfig.plugins.postVue,
      babel({
        ...baseConfig.plugins.babel,
        presets: [
          [
            '@babel/preset-env',
            { modules: false }
          ],
        ],
      }),
    ],
  };

  const merged = {
    input: 'src/index.js',
    external,
    output: {
      format: 'esm',
      file: 'dist/vuelib.esm.js'
    },
    plugins: [
      commonjs(),
      replace(baseConfig.plugins.replace),
      ...baseConfig.plugins.preVue,
      vue(baseConfig.plugins.vue),
      ...baseConfig.plugins.postVue,
      babel({
        ...baseConfig.plugins.babel,
        presets: [
          [
            '@babel/preset-env',
            { modules: false }
          ],
        ],
      }),
    ]
  }
  buildFormats.push(esConfig);
  buildFormats.push(merged);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sets up our configuration for esm builds. module (rollup, webpack) bundlers will pick this builds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsf8z0fjwemi56m6owjte.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsf8z0fjwemi56m6owjte.png" alt="ESM Build output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this we have a single build which has all our code and others are splitted chunks from esm/index.js.&lt;/p&gt;

&lt;p&gt;Also with this we can have treeshaking on project which uses library.&lt;/p&gt;

&lt;p&gt;with both components&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgnvgcc4l499y9shlblrm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgnvgcc4l499y9shlblrm.jpg" alt="With All Components"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is with only one component. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F217ku8iylkm84br2ue2u.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F217ku8iylkm84br2ue2u.jpg" alt="With Single Components"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Only the component which is included comes in the build.&lt;/p&gt;

&lt;p&gt;Now lets add other module configs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
if (!argv.format || argv.format === 'iife') {
  const unpkgConfig = {
    ...baseConfig,
    input: './src/index.js',
    external,
    output: {
      compact: true,
      file: 'dist/vuelib-browser.min.js',
      format: 'iife',
      name: 'vuelib',
      exports: 'named',
      globals,
    },
    plugins: [
      commonjs(),
      replace(baseConfig.plugins.replace),
      ...baseConfig.plugins.preVue,
      vue(baseConfig.plugins.vue),
      ...baseConfig.plugins.postVue,
      babel(baseConfig.plugins.babel),
      terser({
        output: {
          ecma: 5,
        },
      }),
    ],
  };
  buildFormats.push(unpkgConfig);
}

if (!argv.format || argv.format === 'cjs') {
  const cjsConfig = {
    ...baseConfig,
    input: entriespath,
    external,
    output: {
      compact: true,
      format: 'cjs',
      dir: 'dist/cjs',
      exports: 'named',
      globals,
    },
    plugins: [
      commonjs(),
      replace(baseConfig.plugins.replace),
      ...baseConfig.plugins.preVue,
      vue({
        ...baseConfig.plugins.vue,
        template: {
          ...baseConfig.plugins.vue.template,
          optimizeSSR: true,
        },
      }),
      ...baseConfig.plugins.postVue,
      babel(baseConfig.plugins.babel),
    ],
  };
  buildFormats.push(cjsConfig);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets create individual build of each components i.e umd Builds&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const mapComponent = (name) =&amp;gt; {
    return [
      {
        input: baseFolder + componentsFolder + `${name}/index.js`,
        external,
        output: {
          format: 'umd',
          name: capitalize(name),
          file: `dist/components/${name}/index.js`,
          exports: 'named',
          globals,
        },
        plugins: [
          ...baseConfig.plugins.preVue,
          vue({}),
          ...baseConfig.plugins.postVue,
          babel({
            ...baseConfig.plugins.babel,
            presets: [
              [
                '@babel/preset-env',
                { modules: false }
              ],
            ],
          }),
          commonjs(),
        ]
      }
    ]
  }

const ind = [...components.map((f) =&amp;gt; mapComponent(f)).reduce((r, a) =&amp;gt; r.concat(a), [])]

buildFormats = [...buildFormats, ...ind]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now with all build formats done we can export our whole config&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default buildFormats;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets make changes to our package.json&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
"main": "dist/cjs/index.js",
  "module": "dist/esm/index.js",
  "unpkg": "dist/vuelib.min.js",
  "files": [
    "dist",
    "src"
  ],
  "sideEffects": [
    "*.css",
    "*.scss"
  ],
 "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "build:es": "rimraf dist &amp;amp;&amp;amp; cross-env NODE_ENV=production rollup --config rollup.config.js --format es",
    "build:js": "rimraf dist &amp;amp;&amp;amp; cross-env NODE_ENV=production rollup --config rollup.config.js"
  },

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with this each project will be correctly pick formats they need. commonjs projects will pick cjs folder and webpack or rollup ones will pic esm folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffi38caukaqjsa3mnvqac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffi38caukaqjsa3mnvqac.png" alt="dist folder after all builds"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this configuration we can build our library. We have added treeshaking and postcss preprocessors to our library.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: we have inlined the css on the components directly in case we need css in different files e.g bundle.css we need to use plugins like rollup-plugin-scss, rollup-plugin-css-only.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, we have created vue 3 components library with rollup and postcss and it has treeshaking capability.&lt;/p&gt;

&lt;p&gt;Code related to this article is available on &lt;a href="https://github.com/shubhadip/vue3-component-library" rel="noopener noreferrer"&gt;Github&lt;/a&gt; &lt;/p&gt;

</description>
      <category>vue3</category>
      <category>library</category>
      <category>rollup</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Vue 3 Component Library</title>
      <dc:creator>shubhadip</dc:creator>
      <pubDate>Fri, 23 Oct 2020 08:49:54 +0000</pubDate>
      <link>https://dev.to/shubhadip/vue-3-component-library-2le8</link>
      <guid>https://dev.to/shubhadip/vue-3-component-library-2le8</guid>
      <description>&lt;h1&gt;
  
  
  This is part1 of creating a component library using vue 3 and rollup.
&lt;/h1&gt;

&lt;p&gt;We will be using vuejs3, postcss and rollup to build our project.&lt;/p&gt;

&lt;p&gt;vue3 project can be created using vue-cli:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vue create projectName
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we complete all the steps to create our project. our project directory will look something like this &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frg647664rnl1svoi6y7c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frg647664rnl1svoi6y7c.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After this we have to wire up install function which will be used by other projects to rope up our components in them.so lets add index.js which will have install function init.we can read more about install function here &lt;a href="https://v3.vuejs.org/guide/plugins.html#writing-a-plugin" rel="noopener noreferrer"&gt;writing a plugin&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# src/index.ts
import * as components from "./components/index"

const install = (instance) =&amp;gt; {
    for (let componentKey in components) {
        instance.use((components)[componentKey])
    }
}

export default install

export * from './components'

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code we are importing all components from components folder and registering them here with install function.Also all the components are exported so that we can register them with Vue.component().&lt;/p&gt;

&lt;p&gt;Within components folder we will create index.js which will export all our components that are used to register via install function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# components/index.js
import HelloWorld from "./helloworld";
import TestWorld from "./testworld";

export { HelloWorld, TestWorld };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets see our components folder we will be having folder for each components with a .vue file, .css file and an index.js file.Our components folder will look something like this&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F172lddbuq3y18xaqitu6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F172lddbuq3y18xaqitu6.png" alt="Component folder structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Content of index.js will be as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import HelloWorld from "./HelloWorld.vue";

import { registerComponent } from "./../../utils/plugins/index";

const Plugin = {
  install(vue) {
    registerComponent(vue, HelloWorld);
  }
};

export default Plugin;

export { HelloWorld };

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is just to use each component as plugin in our UMD/IIFE build separately, each index.js will act as entry point in our rollup config.&lt;/p&gt;

&lt;p&gt;We will create a util folder which will have &lt;strong&gt;plugin/index.js&lt;/strong&gt; code inside this will help as set global properties and register our component&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# utils/plugins/index.js
export const registerComponent = (instance, component) =&amp;gt; {
    instance.component(component.name, component)
}

export const registerComponentProgrammatic = (instance, property, component) =&amp;gt; {
    if (!instance.config.globalProperties.$test) instance.config.globalProperties.$test = {}
    instance.config.globalProperties.$test[property] = component
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this much setup we can get started with our dev server using yarn serve and start coding our plugin.&lt;/p&gt;

&lt;p&gt;Now we will add postcss to our project so that we can use its plugins. lets install few postcss plugins&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn add -D postcss@8.0.0 postcss-import@12.0.1 postcss-inline-svg@4.1.0 postcss-loader@4.0.4 postcss-nested@4.2.1 postcss-simple-vars@5.0.2 postcss-url@8.0.0 autoprefixer@9.7.8&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;with this done we can setup our postcss.config.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const path = require('path');
module.exports = {
    plugins: {
      "postcss-import": {
        resolve(id, basedir){
           // resolve alias @css, @import '@css/style.css'
            // because @css/ has 5 chars
            if (id.startsWith('@css')) {
              return path.resolve('./src/assets/styles/css', id.slice(5));
            }

            // resolve node_modules, @import '~normalize.css/normalize.css'
            // similar to how css-loader's handling of node_modules
            if (id.startsWith('~')) {
              return path.resolve('./node_modules', id.slice(1));
            }

            // resolve relative path, @import './components/style.css'
            return path.resolve(basedir, id);
        }
      },
      "postcss-simple-vars": {},
      "postcss-nested": {},
      "postcss-url": {},
      autoprefixer: {
        overrideBrowserslist: "&amp;gt; 1%, IE 6, Explorer &amp;gt;= 10, Safari &amp;gt;= 7"
      }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can write css in our .css files and import them in .vue files on our component. We will be adding main.css file in our assets folder which will have css variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# assets/styles/css/main.css
$red: red;
$yellow: yellow;
$blue: blue;
$green: green;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# components/HelloWorld.vue
&amp;lt;style lang="css" scoped&amp;gt;
@import "./helloworld.css";
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# components/helloworld.css
@import "@css/main.css";

.highlight1 {
    color: $red;
}

.imgtest {
  background-image: url("../../assets/img/icons/star-half.svg");
  background-size: 100%;
  width: 100px;
  height: 100px;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can use postcss import, vars and nesting features.We can get started to develop our components with css compiled by postcss.With this setup our project will look like this &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fktpkq67el3gagzqkbnzy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fktpkq67el3gagzqkbnzy.png" alt="Directory sttructure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All updated code for this article is available at &lt;a href="https://github.com/shubhadip/vue3-component-library" rel="noopener noreferrer"&gt;github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In next part we will add rollup to build this project so that we can have treeshaking features.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>vue</category>
      <category>componentlibrary</category>
      <category>postcss</category>
    </item>
  </channel>
</rss>
