<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Louis-Marie Michelin]]></title><description><![CDATA[Louis-Marie Michelin]]></description><link>https://lmichelin.fr/</link><image><url>https://lmichelin.fr/favicon.png</url><title>Louis-Marie Michelin</title><link>https://lmichelin.fr/</link></image><generator>Ghost 3.42</generator><lastBuildDate>Sat, 21 Mar 2026 18:39:29 GMT</lastBuildDate><atom:link href="https://lmichelin.fr/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Configure Stylelint to prevent non-scoped selectors in CSS modules]]></title><description><![CDATA[In this article, I will present you the Stylelint plugin I developed to prevent usage of non-scoped selectors in CSS modules.]]></description><link>https://lmichelin.fr/stylelint-css-modules-no-global-scoped-selector/</link><guid isPermaLink="false">6186e0e613e0cc0c22f7285b</guid><dc:creator><![CDATA[Louis-Marie Michelin]]></dc:creator><pubDate>Sat, 06 Nov 2021 21:58:47 GMT</pubDate><media:content url="https://lmichelin.fr/content/images/2021/11/stylelint-css-modules.png" medium="image"/><content:encoded><![CDATA[<img src="https://lmichelin.fr/content/images/2021/11/stylelint-css-modules.png" alt="Configure Stylelint to prevent non-scoped selectors in CSS modules"><p><strong>TL;DR:</strong> Install this plugin 👉 <a href="https://www.npmjs.com/package/stylelint-css-modules-no-global-scoped-selector">https://www.npmjs.com/package/stylelint-css-modules-no-global-scoped-selector</a></p><h2 id="introduction">Introduction</h2><p>When using <a href="https://github.com/css-modules/css-modules">CSS Modules</a> to apply some style to a React component, there is a risk to impact other components if the selectors are not scoped correctly (using a class or an id).</p><p>A typical case when this issue can happen is with external libraries that require some CSS customization that are not possible using the props exposed by the library. For example, if we use <a href="https://react-select.com/">react-select</a> in our component and we want to customize it, we can directly apply CSS to internal classes according to the <a href="https://react-select.com/styles#using-classnames">docs</a>.</p><p>The problem here is that if we just write <code>.react-select__control { ... }</code> in our CSS module file it will not work because the class will be renamed to something like <code>.react-select__control__XXXX</code> to be scoped. To avoid this behavior, we can add the <code>:global</code> keyword before the class to prevent it to be renamed. This solution works but it's risky because the selector is not scoped anymore: all the <code>react-select</code> used outside the component are also impacted by this style.</p><p>To avoid this issue, the solution is to add a scoped selector before the global keyword: <code>.customReactSelect :global .react-select__control { ... }</code></p><p>In this case the component would look like:</p><figure class="kg-card kg-code-card"><pre><code class="language-scss">.customReactSelect {
  :global .react-select__control {
    // ...
  }
}</code></pre><figcaption>CustomReactSelect.module.scss</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-tsx">import ReactSelect from 'react-select';
import styles from './CustomReactSelect.module.scss';

const CustomReactSelect = () =&gt; (
  &lt;div className={styles.commonReactSelect}&gt;
    &lt;ReactSelect classNamePrefix="react-select" /&gt;
  &lt;/div&gt;
);

// Or even better:
const CustomReactSelect = () =&gt; (
  &lt;ReactSelect
    classNamePrefix="react-select"
    className={styles.commonReactSelect}
  /&gt;
);

export default CustomReactSelect;
</code></pre><figcaption>CustomReactSelect.tsx</figcaption></figure><p>In this example, only this occurrence of <code>react-select</code> will be impacted 🎉.</p><p>But how to be sure non-scoped selectors will never be written in our CSS module files?</p><h2 id="configure-stylelint-to-prevent-usage-of-non-scoped-selectors">Configure Stylelint to prevent usage of non-scoped selectors</h2><p><a href="https://stylelint.io/">Stylelint</a> is a great linting tool for CSS files (like ESLint for JS files). You can run it locally in your terminal, programmatically in your CI pipelines, and you can also integrate it directly in your IDE (like ESLint) so that errors are underlined in red.</p><p> To avoid writing non-scoped selectors in CSS modules I wrote a Stylelint plugin, you can check it here 👉 <a href="https://github.com/lmichelin/stylelint-css-modules-no-global-scoped-selector">https://github.com/lmichelin/stylelint-css-modules-no-global-scoped-selector</a>. Installation steps are detailed in the Readme.</p><p>Once this plugin is integrated into your Stylelint configuration, you can see it in action in VS Code for example (you need to install the <a href="https://marketplace.visualstudio.com/items?itemName=stylelint.vscode-stylelint">Stylelint extension for VS Code</a> first):</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://lmichelin.fr/content/images/2021/11/image.png" class="kg-image" alt="Configure Stylelint to prevent non-scoped selectors in CSS modules" srcset="https://lmichelin.fr/content/images/size/w600/2021/11/image.png 600w, https://lmichelin.fr/content/images/size/w1000/2021/11/image.png 1000w, https://lmichelin.fr/content/images/2021/11/image.png 1029w" sizes="(min-width: 720px) 720px"><figcaption>Non-scoped selector reported by Stylelint in VS Code</figcaption></figure><p>Don't hesitate to integrate this Stylelint plugin in your projects, and feel free to open an issue on <a href="https://github.com/lmichelin/stylelint-css-modules-no-global-scoped-selector">the GitHub repository</a> if you detect a bug or a missing feature!</p>]]></content:encoded></item><item><title><![CDATA[Set Up your Ghost Blog Behind Nginx on a Scaleway Instance]]></title><description><![CDATA[In this article, I will show you how I managed to set up my Ghost blog behind a Nginx server on a Scaleway instance with HTTPS enabled.]]></description><link>https://lmichelin.fr/setup-ghost-blog-scaleway-nginx/</link><guid isPermaLink="false">617ef64c469ce4434cfb88c3</guid><dc:creator><![CDATA[Louis-Marie Michelin]]></dc:creator><pubDate>Mon, 29 Jul 2019 20:13:17 GMT</pubDate><media:content url="https://lmichelin.fr/content/images/2019/07/ghost-scaleway.png" medium="image"/><content:encoded><![CDATA[<img src="https://lmichelin.fr/content/images/2019/07/ghost-scaleway.png" alt="Set Up your Ghost Blog Behind Nginx on a Scaleway Instance"><p>In this article, I will show you how I managed to set up my <a href="https://github.com/TryGhost/Ghost">Ghost blog</a> behind a Nginx server on a <a href="https://www.scaleway.com/virtual-instances/development/">Scaleway instance</a> with HTTPS enabled.</p><h1 id="purchase-a-domain-name">Purchase a domain name</h1><p>The first thing you will need to do is purchase a domain name, like <em>my-awesome-blog.io</em>. HTTPS can't be enabled without it.</p><h1 id="start-your-scaleway-instance">Start your Scaleway instance</h1><p>Create your <a href="https://console.scaleway.com/register">Scaleway account</a> if you don't have one, follow the <a href="https://www.scaleway.com/en/docs/configure-new-ssh-key/">official instructions</a> to add an SSH key, and go to the <a href="https://console.scaleway.com/instance/servers">console</a> to add an instance.</p><p>You can choose a <code>DEV-1S</code> instance to start. If you don't know which OS to choose, you can pick <em>Debian</em>, it's a light and easy OS to start.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://lmichelin.fr/content/images/2019/07/image-13.png" class="kg-image" alt="Set Up your Ghost Blog Behind Nginx on a Scaleway Instance"><figcaption>Create your Scaleway instance</figcaption></figure><p>Click on <code>Create</code> and wait a few seconds for your instance to be created:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://lmichelin.fr/content/images/2019/07/image-12.png" class="kg-image" alt="Set Up your Ghost Blog Behind Nginx on a Scaleway Instance"><figcaption>Starting your Scaleway instance...</figcaption></figure><p>Once your instance is created, you can click on the <code>Console</code> button to see your instance booting:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://lmichelin.fr/content/images/2019/07/image-14.png" class="kg-image" alt="Set Up your Ghost Blog Behind Nginx on a Scaleway Instance"><figcaption>Scaleway instance starting</figcaption></figure><p>When your instance is fully started you can close the console and copy the <em>SSH</em> command:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://lmichelin.fr/content/images/2019/07/image-15.png" class="kg-image" alt="Set Up your Ghost Blog Behind Nginx on a Scaleway Instance"><figcaption>Click on <em>Copy</em> to copy your SSH command</figcaption></figure><p>Paste this command in a terminal on your computer and press ENTER, you are now connected to your instance through SSH! 🚀</p><figure class="kg-card kg-image-card"><img src="https://lmichelin.fr/content/images/2019/07/image-16.png" class="kg-image" alt="Set Up your Ghost Blog Behind Nginx on a Scaleway Instance"></figure><p>You can run <code>apt update &amp;&amp; apt upgrade -y</code> to update the outdated packages.</p><h1 id="install-nginx">Install Nginx</h1><p>Nginx is a popular and powerful web server allowing you to host many websites on your server. Run this command to install it:</p><pre><code>apt install nginx</code></pre><p>You can now visit <code>http://YOUR_SCALEWAY_IP</code> (for example <code>http://51.15.215.187/</code>) and you should see that Nginx is correctly installed!</p><figure class="kg-card kg-image-card"><img src="https://lmichelin.fr/content/images/2019/07/image-17.png" class="kg-image" alt="Set Up your Ghost Blog Behind Nginx on a Scaleway Instance"></figure><h1 id="update-your-dns-zone">Update your DNS zone</h1><p>To access your server with your domain name, you need to add an entry in your DNS zone. Go to your customer account of your domain name provider and add an <strong><code>A</code></strong> entry to your DNS zone to make your domain point to your Scaleway IP address:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://lmichelin.fr/content/images/2019/07/image-18.png" class="kg-image" alt="Set Up your Ghost Blog Behind Nginx on a Scaleway Instance"><figcaption>Add a <strong>A</strong> entry to your DNS zone</figcaption></figure><p>Now, you should be able to access your website with your domain name:</p><figure class="kg-card kg-image-card"><img src="https://lmichelin.fr/content/images/2019/07/image-19.png" class="kg-image" alt="Set Up your Ghost Blog Behind Nginx on a Scaleway Instance"></figure><p>You are now ready to install Ghost!</p><h1 id="install-ghost">Install Ghost</h1><h2 id="install-node-js">Install Node.js</h2><p>Ghost is written in JavaScript, therefore you need to install node.js to run it:</p><pre><code>curl -sL https://deb.nodesource.com/setup_10.x | bash -
apt install -y nodejs</code></pre><h2 id="install-mysql-and-create-a-database">Install MySQL and create a database</h2><p>Ghost uses a MySQL database, so just install <code>mysql-server</code>:</p><pre><code>apt install mysql-server</code></pre><p>Then run <code>mysql</code> and create a new user and his own database:</p><pre><code class="language-sql">CREATE USER 'ghost'@'localhost' IDENTIFIED BY 'changeThisPassword';
CREATE DATABASE ghost;
GRANT ALL ON ghost.* TO 'ghost'@'localhost' IDENTIFIED BY 'changeThisPassword';
FLUSH PRIVILEGES;
exit</code></pre><h2 id="install-ghost-cli">Install Ghost CLI</h2><pre><code>npm install ghost-cli -g</code></pre><h2 id="create-a-non-root-user">Create a non-root user</h2><p>You need to create a non-root user and add it to the <code>sudo</code> group to install Ghost:</p><pre><code>adduser blog
adduser blog sudo</code></pre><h2 id="create-a-directory-for-your-ghost-installation">Create a directory for your Ghost installation</h2><p>We need to create a folder and set the right owner and permissions for it.</p><pre><code>mkdir /var/www/ghost
chown blog:blog /var/www/ghost
chmod 775 /var/www/ghost
cd /var/www/ghost</code></pre><h2 id="log-in-as-the-new-user">Log in as the new user</h2><pre><code>su blog</code></pre><h2 id="install-ghost-1">Install Ghost</h2><pre><code>ghost install</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://lmichelin.fr/content/images/2019/07/image-20.png" class="kg-image" alt="Set Up your Ghost Blog Behind Nginx on a Scaleway Instance"><figcaption>Installing Ghost</figcaption></figure><p>Answer the questions with your own info, and answer <em>Yes</em> to all <code>Y/n</code> questions. Ghost will automatically configure Nginx and install the <a href="http://acme.sh/">acme.sh</a> script to get <a href="https://letsencrypt.org/">Let's Encrypt</a> SSL certificates! 👌</p><figure class="kg-card kg-image-card"><img src="https://lmichelin.fr/content/images/2019/07/image-21.png" class="kg-image" alt="Set Up your Ghost Blog Behind Nginx on a Scaleway Instance"></figure><p>Your blog is now ready! 😎</p><figure class="kg-card kg-image-card"><img src="https://lmichelin.fr/content/images/2019/07/image-23.png" class="kg-image" alt="Set Up your Ghost Blog Behind Nginx on a Scaleway Instance"></figure><p>Create your first post and enjoy your new blog! 😉</p><p><em>Get more information on the <a href="https://ghost.org/docs/">official documentation</a>.</em></p>]]></content:encoded></item><item><title><![CDATA[Setup VS Code for Efficient PHP development 🚀]]></title><description><![CDATA[I recently started programming in PHP using the well-known Symfony framework. I wanted to keep my VS Code habits and proficiency I had from my previous projects in Node.js and Vue.js, so I tried to configure VS Code for PHP development as well.]]></description><link>https://lmichelin.fr/vscode-php-development/</link><guid isPermaLink="false">617ef64c469ce4434cfb88c2</guid><dc:creator><![CDATA[Louis-Marie Michelin]]></dc:creator><pubDate>Sun, 21 Jul 2019 12:58:55 GMT</pubDate><media:content url="https://lmichelin.fr/content/images/2019/07/vscode-php-xdebug-extension-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://lmichelin.fr/content/images/2019/07/vscode-php-xdebug-extension-1.png" alt="Setup VS Code for Efficient PHP development 🚀"><p>This article has moved here:<br>👉 <a href="https://blog.theodo.com/2019/07/vscode-php-development/">https://blog.theodo.com/2019/07/vscode-php-development/</a> 😉</p>]]></content:encoded></item><item><title><![CDATA[Use jsconfig.json to improve VS Code IntelliSense on a Vue.js or Nuxt.js project]]></title><description><![CDATA[When you work on a Vue.js or a Nuxt.js project, and more generally on a webpack project, you usually use absolute imports. Learn how to configure the jsconfig.json file in Visual Studio Code for Vue.js or Nuxt.js development and make IntelliSense work with absolute imports!]]></description><link>https://lmichelin.fr/vscode-intellisense-jsconfig-vue-js-nuxt-js-absolute-imports/</link><guid isPermaLink="false">617ef64c469ce4434cfb88c0</guid><dc:creator><![CDATA[Louis-Marie Michelin]]></dc:creator><pubDate>Sat, 15 Jun 2019 15:18:46 GMT</pubDate><media:content url="https://lmichelin.fr/content/images/2019/06/nuxt.svg" medium="image"/><content:encoded><![CDATA[<img src="https://lmichelin.fr/content/images/2019/06/nuxt.svg" alt="Use jsconfig.json to improve VS Code IntelliSense on a Vue.js or Nuxt.js project"><p>When you work on a Vue.js or a Nuxt.js project, and more generally on a webpack project, you usually use absolute imports like</p><p><code>import MyComponent from '~/components/MyComponent.vue'</code> </p><p>instead of</p><p><code>import MyComponent from '../../components/MyComponent.vue'</code></p><p>in which the number of <code>../</code> depends on the folder where the import is needed. These relative imports are not recommended because they are complicated to maintain in case of refactoring, but Visual Studio Code likes them because the editor can know exactly where the files are located and so can provide a complete IntelliSense experience (code suggestions, auto-imports, go to definition, etc.).</p><p>Unfortunately, even if you have the <a href="https://marketplace.visualstudio.com/items?itemName=octref.vetur">Vetur</a> extension installed, IntelliSense (code completion, go to definition, ...) doesn't work by default when working with absolute imports. You need to give some informations to VS Code about absolute paths you use for imports. It can be done by adding a <code>jsconfig.json</code> file in your project. In this file you will specify a mapping table to link the virtual paths you use in your imports (like <code>@/...</code> or <code>~/...</code> ) with the real paths of your project. The paths specified in the <code>jsconfig.json</code> file are relative to the location of this file, therefore you should place it in the root folder of your project.</p><!--kg-card-begin: markdown--><p>According to the <a href="https://nuxtjs.org/guide/directory-structure#aliases">Nuxt.js documentation</a>, they are 4 aliases in a Nuxt.js project:</p>
<ul>
<li><code>~</code> or <code>@</code> for <code>srcDir</code> (the src folder, the same as <code>rootDir</code> by default)</li>
<li><code>~~</code> or <code>@@</code> for <code>rootDir</code> (the project's root folder)</li>
</ul>
<!--kg-card-end: markdown--><p>For a Vue.js project generated with <code>@vue/cli</code>, <code>@</code> is mapped with <code>src/</code>.</p><p>Therefore, in the case of a Nuxt.js project, you can use the following <code>jsconfig.json</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-json">{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "~/*": ["./*"],
      "@/*": ["./*"],
      "~~/*": ["./*"],
      "@@/*": ["./*"]
    }
  },
  "exclude": ["node_modules", ".nuxt", "dist"]
}</code></pre><figcaption>jsconfig.json for a Nuxt.js project</figcaption></figure><p>However, if your <code>srcDir</code> is different from <code>rootDir</code>, for example if you have set up <code>srcDir: 'src/'</code> in your <code>nuxt.config.js</code>, your <code>jsconfig.json</code> would become:</p><figure class="kg-card kg-code-card"><pre><code class="language-json">{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "~/*": ["./src/*"],
      "@/*": ["./src/*"],
      "~~/*": ["./*"],
      "@@/*": ["./*"]
    }
  },
  "exclude": ["node_modules", ".nuxt", "dist"]
}</code></pre><figcaption>jsconfig.json for a Nuxt.js project with custom srcDir</figcaption></figure><p>For a Vue.js project, it would look like:</p><figure class="kg-card kg-code-card"><pre><code class="language-json">{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
    }
  },
  "exclude": ["node_modules", "dist"]
}</code></pre><figcaption>jsconfig.json for a Vue.js project</figcaption></figure><p>The <code>exclude</code> property allows to improve the performance of IntelliSense by specifying folders which are not part of your source code.</p><p>The result: 😎</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://lmichelin.fr/content/images/2019/06/Peek-2019-06-15-17-13.gif" class="kg-image" alt="Use jsconfig.json to improve VS Code IntelliSense on a Vue.js or Nuxt.js project"><figcaption>Auto-completion with absolute imports</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://lmichelin.fr/content/images/2019/06/Peek-2019-06-15-17-15.gif" class="kg-image" alt="Use jsconfig.json to improve VS Code IntelliSense on a Vue.js or Nuxt.js project"><figcaption>Go to definition with absolute imports</figcaption></figure><p><em>A last thing to notice: If you want to have a complete IntelliSense for </em><code><em>.vue</em></code><em> files, you need to install the </em><a href="https://marketplace.visualstudio.com/items?itemName=octref.vetur"><em>Vetur</em></a><em> extension, and your Nuxt.js project must be the root folder of your VS Code workspace. Working in a Nuxt.js project in a subfolder of your VS Code workspace is not supported yet by Vetur (see the </em><a href="https://github.com/vuejs/vetur/issues/904"><em>issue</em></a><em>).</em></p><p>More information: <a href="https://code.visualstudio.com/docs/languages/jsconfig">https://code.visualstudio.com/docs/languages/jsconfig</a></p>]]></content:encoded></item><item><title><![CDATA[Open GitHub files links directly in VS Code and speed up your code reviews! 😎]]></title><description><![CDATA[I developed a browser extension allowing to open GitHub links directly in your IDE at the desired line. Install it and speed up your code reviews!]]></description><link>https://lmichelin.fr/open-github-files-links-directly-in-your-ide/</link><guid isPermaLink="false">617ef64c469ce4434cfb88bf</guid><dc:creator><![CDATA[Louis-Marie Michelin]]></dc:creator><pubDate>Wed, 05 Jun 2019 23:14:16 GMT</pubDate><media:content url="https://lmichelin.fr/content/images/2019/06/vscode-github.png" medium="image"/><content:encoded><![CDATA[<img src="https://lmichelin.fr/content/images/2019/06/vscode-github.png" alt="Open GitHub files links directly in VS Code and speed up your code reviews! 😎"><p>When it comes to do some corrections after a code review, it can be painful to switch between GitHub and your IDE and browse the file tree to find the right file to update.</p><p>To improve this workflow, I recently developed a browser extension allowing to open GitHub links directly in VS Code and put the cursor at the desired line when possible.</p><p>Obviously this extension will only work if the repository is cloned on your computer. You can edit the local repositories path in the extension settings.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://lmichelin.fr/content/images/2019/06/image.png" class="kg-image" alt="Open GitHub files links directly in VS Code and speed up your code reviews! 😎"><figcaption>Click on the VSCode icon to open the file at the right line</figcaption></figure><p>The extension is available on the <a href="https://chrome.google.com/webstore/detail/open-github-in-ide/bmifnnfmccmleigpaolofacllndmfned">Google Chrome Web Store</a> and on <a href="https://addons.mozilla.org/firefox/addon/open-github-in-ide/">Firefox Add-ons</a>.</p><p>Feel free to open an issue on <a href="https://github.com/lmichelin/open-github-links-in-ide">the GitHub repository</a> if you detect a bug or a missing feature!</p>]]></content:encoded></item><item><title><![CDATA[Hello, World!]]></title><description><![CDATA[Here is my first post on my new personal blog!]]></description><link>https://lmichelin.fr/hello-world/</link><guid isPermaLink="false">617ef64c469ce4434cfb88bd</guid><dc:creator><![CDATA[Louis-Marie Michelin]]></dc:creator><pubDate>Tue, 28 May 2019 19:51:00 GMT</pubDate><media:content url="https://lmichelin.fr/content/images/2019/05/5c1bb7dd5e7cc9678fcdc39f_Hello-World-Header.png" medium="image"/><content:encoded><![CDATA[<img src="https://lmichelin.fr/content/images/2019/05/5c1bb7dd5e7cc9678fcdc39f_Hello-World-Header.png" alt="Hello, World!"><p>Here is my first post on my new personal blog!</p><p>Inline code example: <code>console.log('hello!')</code>.</p><blockquote>Code example with syntax highlighting:</blockquote><!--kg-card-begin: markdown--><pre><code class="language-js">const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) =&gt; res.send('Hello World!'))

app.listen(port, () =&gt; console.log(`Express app listening on port ${port}!`))
</code></pre>
<!--kg-card-end: markdown--><p>More posts coming soon! 😉</p>]]></content:encoded></item></channel></rss>