<?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"><channel><title><![CDATA[Christian's Blog]]></title><description><![CDATA[Christian's Blog]]></description><link>https://hashnode.christian-hollaender.de</link><generator>RSS for Node</generator><lastBuildDate>Wed, 13 May 2026 17:13:40 GMT</lastBuildDate><atom:link href="https://hashnode.christian-hollaender.de/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to create a shared Prettier configuration]]></title><description><![CDATA[This post was originally posted over on my personal website. Maybe you want to check it out [HERE].


Keeping configs for styling and linting tools consistent across multiple projects can be challenging. Many tools support shared configs, which are c...]]></description><link>https://hashnode.christian-hollaender.de/how-to-create-a-shared-prettier-configuration</link><guid isPermaLink="true">https://hashnode.christian-hollaender.de/how-to-create-a-shared-prettier-configuration</guid><dc:creator><![CDATA[Christian Holländer]]></dc:creator><pubDate>Wed, 30 Nov 2022 19:10:04 GMT</pubDate><enclosure url="https://christian-hollaender.de/posts/2-how-to-create-a-shared-prettier-configuration/teaserImage" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>This post was originally posted over on my personal website. Maybe you want to check it out <a target="_blank" href="https://christian-hollaender.de/posts/2-how-to-create-a-shared-prettier-configuration">[HERE]</a>.</p>
</blockquote>
<hr />
<p>Keeping configs for styling and linting tools consistent across multiple projects can be challenging. Many tools support shared configs, which are created once and used everywhere. This article will discover how to create a shared configuration for prettier.</p>
<hr />
<h2 id="heading-motivation">Motivation</h2>
<p>Recently we decided, that we want to have a common styling on how to write our Typescript code. This would make it easier for us to read and understand code others had written or even that we have written ourselves a few months ago. Therefore we agreed on integrating <a target="_blank" href="https://prettier.io/">Prettier</a>. An opinionated code formatter.</p>
<p>When we first adopted prettier on all our dozens of projects we copy/pasted the configuration from one project to the other. As we updated the config to fit our needs we were faced with inconsistent options in our projects. So we needed to figure out, how to change the prettier options globally for all our projects at the same time. Luckily the prettier maintainers got you covered. They already thought of that problem and provide a native way to share the config across multiple projects by creating a separate npm package. (<a target="_blank" href="https://prettier.io/docs/en/configuration.html#sharing-configurations">Sharing Configurations</a>)</p>
<p>In this guide, I want to show you how we structured our shared config to meet several projects with different prettier plugins, like the phenomenal <a target="_blank" href="https://github.com/tailwindlabs/prettier-plugin-tailwindcss">Tailwind Prettier plugin</a>. But also to make it extensible if a project needs some special config options only for this specific project.</p>
<h2 id="heading-get-started">Get Started</h2>
<p>First of all, we need to initialize our repository. As I love typescript I wanted that the shared config itself is written in typescript and gets transpiled to javascript through a built-step. I will do this by using Rollup.js, but Webpack should do the trick as well. So let’s get started..</p>
<p>Installing Typescript should be straightforward by running <code>npm i -D typescript</code> and creating a <code>tsconfig.json</code> in the project root. I am using this one:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"compilerOptions"</span>: {
    <span class="hljs-attr">"target"</span>: <span class="hljs-string">"es6"</span>,
    <span class="hljs-attr">"module"</span>: <span class="hljs-string">"ESNext"</span>,
    <span class="hljs-attr">"strict"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"importHelpers"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"moduleResolution"</span>: <span class="hljs-string">"node"</span>,
    <span class="hljs-attr">"experimentalDecorators"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"emitDecoratorMetadata"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"skipLibCheck"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"esModuleInterop"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"allowSyntheticDefaultImports"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"sourceMap"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"outDir"</span>: <span class="hljs-string">"dist"</span>,
    <span class="hljs-attr">"declaration"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"declarationDir"</span>: <span class="hljs-string">"dist/types"</span>,
    <span class="hljs-attr">"declarationMap"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"resolveJsonModule"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"baseUrl"</span>: <span class="hljs-string">"."</span>,
    <span class="hljs-attr">"typeRoots"</span>: [
      <span class="hljs-string">"node_modules/@types"</span>
    ],
    <span class="hljs-attr">"lib"</span>: [
      <span class="hljs-string">"es2015"</span>,
      <span class="hljs-string">"es2017"</span>
    ]
  },
  <span class="hljs-attr">"include"</span>: [
    <span class="hljs-string">"src/**/*.ts"</span>
  ],
  <span class="hljs-attr">"exclude"</span>: [
    <span class="hljs-string">"node_modules"</span>
  ]
}
</code></pre>
<p>Now I want to initialize the Rollup.js setup by adding the <code>rollup.config.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> excludeDependenciesFromBundle <span class="hljs-keyword">from</span> <span class="hljs-string">'rollup-plugin-exclude-dependencies-from-bundle'</span>;
<span class="hljs-keyword">import</span> resolve <span class="hljs-keyword">from</span> <span class="hljs-string">'@rollup/plugin-node-resolve'</span>;
<span class="hljs-keyword">import</span> commonjs <span class="hljs-keyword">from</span> <span class="hljs-string">'@rollup/plugin-commonjs'</span>;
<span class="hljs-keyword">import</span> typescript <span class="hljs-keyword">from</span> <span class="hljs-string">'rollup-plugin-typescript2'</span>;
<span class="hljs-keyword">import</span> { visualizer } <span class="hljs-keyword">from</span> <span class="hljs-string">'rollup-plugin-visualizer'</span>;

<span class="hljs-comment">// eslint-disable-next-line @typescript-eslint/no-var-requires</span>
<span class="hljs-keyword">const</span> packageJson = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./package.json'</span>);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
    <span class="hljs-attr">input</span>: <span class="hljs-string">'src/index.ts'</span>,
    <span class="hljs-attr">inlineDynamicImports</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">output</span>: [
        {
            <span class="hljs-attr">file</span>: packageJson.main,
            <span class="hljs-attr">format</span>: <span class="hljs-string">'cjs'</span>,
            <span class="hljs-attr">exports</span>: <span class="hljs-string">'default'</span>,
            <span class="hljs-attr">sourcemap</span>: <span class="hljs-literal">true</span>,
        },
    ],
    <span class="hljs-attr">plugins</span>: [
        excludeDependenciesFromBundle(),
        resolve(),
        commonjs(),
        typescript({
            <span class="hljs-attr">useTsconfigDeclarationDir</span>: <span class="hljs-literal">true</span>,
        }),
        visualizer({
            <span class="hljs-attr">filename</span>: <span class="hljs-string">'package-deps.html'</span>,
            <span class="hljs-attr">template</span>: <span class="hljs-string">'sunburst'</span>,
            <span class="hljs-attr">gzipSize</span>: <span class="hljs-literal">true</span>,
            <span class="hljs-attr">brotliSize</span>: <span class="hljs-literal">true</span>,
        }),
    ],
};
</code></pre>
<blockquote>
<p>I will skip the installation of the necessary dependencies, as I think you already know how to do this when you are interested in creating a shared prettier config ;)</p>
</blockquote>
<p>Now we are finally ready to actually create the shared config in our <code>src</code> folder:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Config } <span class="hljs-keyword">from</span> <span class="hljs-string">'prettier'</span>;

<span class="hljs-keyword">const</span> defaultConfig: Config = {
    singleQuote: <span class="hljs-literal">true</span>,
    bracketSameLine: <span class="hljs-literal">true</span>,
    tabWidth: <span class="hljs-number">4</span>,
    plugins: [],
    overrides: [
        {
            files: <span class="hljs-string">'**/*.{json,yml,yaml}'</span>,
            options: {
                tabWidth: <span class="hljs-number">2</span>,
            },
        },
        {
            files: <span class="hljs-string">'**/*.{yml,yaml}'</span>,
            options: {
                singleQuote: <span class="hljs-literal">false</span>,
                bracketSpacing: <span class="hljs-literal">false</span>,
            },
        },
    ],
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defaultConfig;
</code></pre>
<p>Last but not least I want to show you a cool little trick to use the just-built config right in this project. When do this by creating a <code>prettier.config.js</code> file in our project root. In there we put the following content:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('prettier').Options}</span> </span>*/</span>
<span class="hljs-built_in">module</span>.exports = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./dist/prettier-config'</span>);
</code></pre>
<p>We are actually just importing our build module and re-export it so our prettier command can find it. But note: You need to build before you can apply the changes of the shared config to the project because there is a built-step involved!</p>
]]></content:encoded></item><item><title><![CDATA[Automatic Package Releases using Semantic-Release]]></title><description><![CDATA[This post was originally posted over on my personal website. Maybe you want to check it out [HERE].


Writing the release notes and Changelog manually is a time-consuming and complicated task. And then you need to push your new release to your desire...]]></description><link>https://hashnode.christian-hollaender.de/automatic-package-releases-using-semantic-release</link><guid isPermaLink="true">https://hashnode.christian-hollaender.de/automatic-package-releases-using-semantic-release</guid><dc:creator><![CDATA[Christian Holländer]]></dc:creator><pubDate>Wed, 30 Nov 2022 19:09:12 GMT</pubDate><enclosure url="https://christian-hollaender.de/posts/1-automatic-package-releases-using-semantic-release/teaserImage" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>This post was originally posted over on my personal website. Maybe you want to check it out <a target="_blank" href="https://christian-hollaender.de/posts/1-automatic-package-releases-using-semantic-release">[HERE]</a>.</p>
</blockquote>
<hr />
<p>Writing the release notes and Changelog manually is a time-consuming and complicated task. And then you need to push your new release to your desired Package-registry like NPM or Packagist. Fortunately, there is a better way to do this. Join me in discovering an alternative.</p>
<hr />
<p>When you already maintained an open-source package or developed a package at work, you might know how complicated the job of releasing a new version can be. Most of the time the process contains the following steps:</p>
<ol>
<li>Scim all Commits, Issues, and PRs to see what changed</li>
<li>Create a list of the previously collected changes for your Changelog and Release-Notes</li>
<li>Decide if you want to create a patch, minor or major release (and probably have to ask the author of the changes)</li>
<li>If your package provides the version to its users, update it</li>
<li>Add a new Git tag to the repository and let your CI/CD build one last time</li>
<li>Push the newly built package to the Registry of your liking (don't forget to authenticate 😉)</li>
</ol>
<p>Fortunately, there is an easy way to automate all of this. It's called CI/CD. Personally, I use Gitlab CI/CD as all my Code is hosted on Gitlab. But Github Actions and other providers should work similarly. So now we need to think about how we can achieve all our tasks in an automated manner. We as developers are lazy people, so writing a bunch of bash scripts all ourselves is not really an option. Luckily there is a tool we can use to help out:</p>
<h4 id="heading-semantic-releasehttpsgithubcomsemantic-releasesemantic-release"><a target="_blank" href="https://github.com/semantic-release/semantic-release">Semantic-Release</a></h4>
<p>Semantic-Release is a tool that automates most of the hard tasks needed to release a new version of our package. It actually can also release new versions of our application, but that is a bit more difficult. Its plugin system makes it easy and extendable for multiple package managers and steps.</p>
<h3 id="heading-so-lets-get-started">So let's get started:</h3>
<p>For this example we want to publish a new NPM package as this is the easiest, to begin with.</p>
<p>First, install the necessary packages:</p>
<pre><code class="lang-bash">npm install -D semantic-release @semantic-release/git @semantic-release/gitlab @semantic-release/npm
</code></pre>
<p>Here I installed the main package <code>semantic-release</code> with some plugins that I need for my setup. There is a good list of official and community plugins on the documentation page of the project: https://semantic-release.gitbook.io/semantic-release/extending/plugins-list</p>
<p>You can also add a script section to your <code>package.json</code>. For me, it was actually necessary. Otherwise, I could not run the command inside my CI/CD environment. Because it was not in my <code>PATH</code>:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"semantic-release"</span>: <span class="hljs-string">"semantic-release"</span>
  }
}
</code></pre>
<p>Now that we can run Semantic Release, we need to configure the project so that releasing works. This is especially important if you don't want to push a package into the normal NPM registry, but want to use the GitLab registry as I do.
For this, we have to adjust the "publishConfig" in the <code>package.json</code>. Enter there the URL of your registry. For me, this is the project-specific URL of the GitLab registry.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"publishConfig"</span>: {
    <span class="hljs-attr">"@&lt;package-scope&gt;:registry"</span>: <span class="hljs-string">"https://gitlab.com/api/v4/projects/&lt;gitlab-project-id&gt;/packages/npm/"</span>
  }
}
</code></pre>
<blockquote>
<p>Do not set the <code>private</code> attribute in your <code>package.json</code> to true, even though it is an internal/private package. This will prevent Semantic-Release from publishing your package to ANY registry.</p>
</blockquote>
<p>After that, we still need to authenticate against our internal registry. For this, we use the so-called <code>.npmrc</code> file. In this file, we configure our package scope with a target URL and a personal access token for authentication. I found it easiest to put the scope definition directly into the repo. The token setting must never happen in the repo, otherwise, you expose the token to possibly unauthorized third parties.</p>
<pre><code>@&lt;package-scope&gt;:registry=https:<span class="hljs-comment">//gitlab.com/api/v4/projects/&lt;gitlab-project-id&gt;/packages/npm/</span>
</code></pre><p>Then you can set your personal access token for local development in your <code>~/.npmrc</code> file. This way you can also authenticate in other projects on your machine and you do not accidentally commit your token to the repo. You can do it like this:</p>
<pre><code><span class="hljs-comment">//gitlab.com/api/v4/packages/npm/:_authToken=&lt;access-token&gt;</span>
</code></pre><blockquote>
<p>Yes the syntax with the <code>//</code> at the beginning is correct. It is rather strange, but this is how it is.</p>
</blockquote>
<p>As we also need the token in our CI/CD we need to set this inside our pipeline job. I did this like so:</p>
<pre><code class="lang-yaml">  <span class="hljs-attr">before_script:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">echo</span> <span class="hljs-string">"@${CI_PROJECT_ROOT_NAMESPACE}:registry=${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/npm/"</span><span class="hljs-string">&gt;.npmrc</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">echo</span> <span class="hljs-string">"${CI_API_V4_URL#https?}/packages/npm/:_authToken=${CI_JOB_TOKEN}"</span><span class="hljs-string">&gt;&gt;.npmrc</span>
</code></pre>
<p>I completely overwrite the <code>.npmrc</code> with variables provided by the CI environment. The nice thing about GitLab is, that it already provides all necessary tokens and URLs. I'll show the complete Job-configuration later on.</p>
<p>Now that we configured everything for NPM to publish a package, we also need to configure Semantic-Release so that we can trigger the releases correctly. Therefore we can use a variety of file formats. The most popular ones are JSON and Javascript. You can find out more <a target="_blank" href="https://semantic-release.gitbook.io/semantic-release/usage/configuration#configuration-file">here</a>. For this demo I chose <code>release.config.js</code> cause it gives us the most dynamic way to configure Semantic-Release:</p>
<pre><code class="lang-js"><span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import("semantic-release").Options }</span> </span>*/</span>
<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">plugins</span>: [
    <span class="hljs-string">"@semantic-release/commit-analyzer"</span>,
    <span class="hljs-string">"@semantic-release/release-notes-generator"</span>,
    <span class="hljs-string">"@semantic-release/gitlab"</span>,
    <span class="hljs-string">"@semantic-release/npm"</span>
  ],
  <span class="hljs-string">"branches"</span>: [
    <span class="hljs-string">"+([0-9])?(.{+([0-9]),x}).x"</span>,
    <span class="hljs-string">"main"</span>,
    <span class="hljs-string">"next"</span>,
    <span class="hljs-string">"next-major"</span>,
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"beta"</span>,
      <span class="hljs-attr">prerelease</span>: <span class="hljs-literal">true</span>,
    },
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"alpha"</span>,
      <span class="hljs-attr">prerelease</span>: <span class="hljs-literal">true</span>,
    }
  ],
};
</code></pre>
<blockquote>
<p>Configuring the branches is currently only necessary because we already use the new git naming for our default branch "main". If you are still using "master", you won't need to configure them.</p>
<p>If you are using TypeScript like me, you can also install <code>@types/semantic-release</code> and use the shown type doc, to get autocompletion in most IDEs.</p>
</blockquote>
<h2 id="heading-add-ci-job">Add CI job</h2>
<p>Last but not least we need to configure our already mentioned CI-Job. This depends on your environment. As I am using Gitlab-CI/CD I can only show how this works:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">release:</span>
  <span class="hljs-attr">stage:</span> <span class="hljs-string">release</span>
  <span class="hljs-attr">image:</span> <span class="hljs-string">node:18.7</span>
  <span class="hljs-attr">only:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>
  <span class="hljs-attr">before_script:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">echo</span> <span class="hljs-string">"@${CI_PROJECT_ROOT_NAMESPACE}:registry=${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/npm/"</span><span class="hljs-string">&gt;.npmrc</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">echo</span> <span class="hljs-string">"${CI_API_V4_URL#https?}/packages/npm/:_authToken=${CI_JOB_TOKEN}"</span><span class="hljs-string">&gt;&gt;.npmrc</span>
  <span class="hljs-attr">script:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">semantic-release</span>
</code></pre>
<p>One thing you will also need to set is an environment Token called: <code>GITLAB_TOKEN</code>. This is needed for the Gitlab-plugin and to be able to upload Releases to Gitlab. You can find these in the left menu under <code>Deployments &gt; Releases</code>.</p>
<h3 id="heading-workflow">Workflow</h3>
<p>To use this automatic release setup, you need to use a special Commit syntax. It is actually quite simple, but you need to memorize all the different keys. Like if you write a commit message like <code>feat: some dope new feature</code>, Semantic-Release will trigger a minor update of your package. If you use <code>fix: some small bug fix</code>, it will trigger a patch release. You can also always do a major release by adding <code>BREAKING CHANGE: Description what changes.</code> to your commit. For example:</p>
<pre><code>feat: we added a non backward compatible change

BREAKING CHANGE: Please now use <span class="hljs-built_in">this</span> and that.
</code></pre><h2 id="heading-conclusion">Conclusion</h2>
<p>This should be it. Now you are all set to automatically release your package with the special Commit-Syntax.
If you want to know more I highly suggest reading through the amazing <a target="_blank" href="https://semantic-release.gitbook.io/semantic-release/">documentation</a>. I have already mentioned it here and there throughout this post.</p>
<p>And now the only thing left is to say thank you for reading!</p>
]]></content:encoded></item></channel></rss>