<?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: Carlos</title>
    <description>The latest articles on DEV Community by Carlos (@cbsumastre).</description>
    <link>https://dev.to/cbsumastre</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4151992%2F9bca355b-a646-41e2-a289-c69268de7a44.png</url>
      <title>DEV Community: Carlos</title>
      <link>https://dev.to/cbsumastre</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cbsumastre"/>
    <language>en</language>
    <item>
      <title>I wrote a VS Code extension because my settings.json kept getting shuffled</title>
      <dc:creator>Carlos</dc:creator>
      <pubDate>Mon, 09 Nov 2026 17:13:56 +0000</pubDate>
      <link>https://dev.to/cbsumastre/i-wrote-a-vs-code-extension-because-my-settingsjson-kept-getting-shuffled-1b0a</link>
      <guid>https://dev.to/cbsumastre/i-wrote-a-vs-code-extension-because-my-settingsjson-kept-getting-shuffled-1b0a</guid>
      <description>&lt;p&gt;I usually have three or four VS Code windows open at once. The API, the frontend, some side project, and whatever repo I opened "just to check something" two hours ago. They all look the same, so I'd regularly type into the wrong one.&lt;/p&gt;

&lt;p&gt;The fix everybody knows is to give each workspace its own color. Blue title bar for the API, orange for the frontend, and so on. There are extensions for that and they work fine.&lt;/p&gt;

&lt;p&gt;My problem was with what they did to &lt;code&gt;.vscode/settings.json&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The diff that annoyed me
&lt;/h2&gt;

&lt;p&gt;In a couple of my projects &lt;code&gt;.vscode/settings.json&lt;/code&gt; is committed. I keep it organized by hand: related keys together, a comment here and there explaining why something is set. Every time I changed the color, the diff looked something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;   "workbench.colorCustomizations": {
&lt;span class="gd"&gt;-    // My own tweaks, grouped on purpose
&lt;/span&gt;     "editor.lineHighlightBackground": "#ffffff0d",
     "editorCursor.foreground": "#ffcc00",
&lt;span class="gd"&gt;-    // Workspace tint
-    "statusBar.background": "#2f6fd6",
&lt;/span&gt;&lt;span class="gi"&gt;+    "terminal.background": "#1b1b1f",
+    "statusBar.background": "#2e8b57",
&lt;/span&gt;     "statusBar.foreground": "#e7e7e7",
&lt;span class="gd"&gt;-    "statusBarItem.hoverBackground": "#447dda",
-    "terminal.background": "#1b1b1f"
&lt;/span&gt;&lt;span class="gi"&gt;+    "statusBarItem.hoverBackground": "#439768"
&lt;/span&gt;   }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two values changed. The diff touches eight lines, both comments are gone, and &lt;code&gt;terminal.background&lt;/code&gt;, which I never touched, jumped to the middle of the block.&lt;/p&gt;

&lt;p&gt;It's not a bug in those extensions, really. It's how the VS Code API works. When you call&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getConfiguration&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;workbench.colorCustomizations&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you're handing VS Code a brand new object, and it writes that object in place of the old one. Whatever order the keys have in &lt;code&gt;newObject&lt;/code&gt; is the order you get in the file, and comments inside it are simply dropped because a JS object has nowhere to keep them.&lt;/p&gt;

&lt;p&gt;So I wrote my own extension, &lt;a href="https://marketplace.visualstudio.com/items?itemName=cbsumastre-es.tidy-tint" rel="noopener noreferrer"&gt;Tidy Tint&lt;/a&gt;. It does the same job (color the activity bar, title bar and status bar per workspace) but it never calls &lt;code&gt;update()&lt;/code&gt; for the colors. It edits the file as text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Editing JSON as text without going crazy
&lt;/h2&gt;

&lt;p&gt;Doing string surgery on JSON by hand sounds like a terrible idea, and it is. Luckily VS Code itself solves this problem with &lt;a href="https://github.com/microsoft/node-jsonc-parser" rel="noopener noreferrer"&gt;&lt;code&gt;jsonc-parser&lt;/code&gt;&lt;/a&gt;, the same library it uses to read your settings. It understands comments and trailing commas, and it has a &lt;code&gt;modify()&lt;/code&gt; function that, instead of returning a new document, returns a list of &lt;em&gt;edits&lt;/em&gt;: "at offset X, replace N characters with this string".&lt;/p&gt;

&lt;p&gt;That's exactly what I wanted. The rules I ended up with are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if a key already exists, only its value changes, in place;&lt;/li&gt;
&lt;li&gt;if it's new, it goes at the end of its object;&lt;/li&gt;
&lt;li&gt;removing colors deletes only the keys Tidy Tint wrote, with their comma and their line;&lt;/li&gt;
&lt;li&gt;nothing else in the file is touched.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first version was basically &lt;code&gt;applyEdits(text, modify(text, path, value, { formattingOptions }))&lt;/code&gt; in a loop, and it almost worked. The catch is that when you pass formatting options, &lt;code&gt;jsonc-parser&lt;/code&gt; sometimes reformats the lines &lt;em&gt;around&lt;/em&gt; the edit too. Not much, but enough to show up in the diff, which was the whole thing I was trying to avoid.&lt;/p&gt;

&lt;p&gt;The workaround was to ask for the edits without formatting and then format only the text I just inserted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;applyMinimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;edits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Edit&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormattingOptions&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;edits&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;applyEdits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The edits are applied from the end of the file backwards so earlier offsets stay valid. The formatting options aren't hardcoded either: the extension looks at the file first and copies whatever it finds (tabs or spaces, indent width, &lt;code&gt;\n&lt;/code&gt; or &lt;code&gt;\r\n&lt;/code&gt;). If your file uses tabs and CRLF, so do the new lines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deleting a key is the annoying part
&lt;/h2&gt;

&lt;p&gt;Adding and changing values was the easy half. Removing them cleanly took more fiddling than I expected, because of commas.&lt;/p&gt;

&lt;p&gt;If the key is in the middle of the object, you delete from the start of its line to the start of the next key. If it's the last one, you have to eat the comma of the &lt;em&gt;previous&lt;/em&gt; key instead, otherwise you're left with a trailing comma (valid in JSONC, but ugly and not what was there before). And if it's the only key, the object just becomes &lt;code&gt;{}&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;siblings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Only property: the object becomes "{}".&lt;/span&gt;
  &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;siblings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Another one follows: delete from this property to the start of the next.&lt;/span&gt;
  &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lineStartIfOnlyIndent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lineStartIfOnlyIndent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;siblings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// It's the last one: delete from the end of the previous one (including its comma).&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;siblings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run "Remove Colors" and &lt;code&gt;workbench.colorCustomizations&lt;/code&gt; ends up empty, the extension removes that key too, so you don't keep a lonely &lt;code&gt;{}&lt;/code&gt; hanging around. If you had your own keys in there (like my cursor color or the terminal background) they stay exactly where they were.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing the file without breaking undo
&lt;/h2&gt;

&lt;p&gt;The last detail: once I have the new text, I don't write it to disk with &lt;code&gt;fs&lt;/code&gt;. I apply it with a &lt;code&gt;WorkspaceEdit&lt;/code&gt; and then save the document. Two reasons. If you have &lt;code&gt;settings.json&lt;/code&gt; open with unsaved changes, writing behind VS Code's back is asking for trouble. And with a &lt;code&gt;WorkspaceEdit&lt;/code&gt; the change goes into the editor's history, so Ctrl+Z (Cmd+Z on Mac) undoes a color change like any other edit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The colors
&lt;/h2&gt;

&lt;p&gt;This part is less interesting but I'll mention it. You pick one base color and the extension derives the rest: foreground is light or dark depending on the base's luminance (the WCAG formula), inactive title bar is the same color with some transparency, the badge on the activity bar uses the hue rotated 150° so it stands out, and the hover color on the status bar is slightly lighter or darker. There are 13 keys in total, and those 13 are the only ones Tidy Tint will ever write or delete.&lt;/p&gt;

&lt;p&gt;The random color isn't fully random either. It picks a random hue but keeps saturation and lightness in a range that doesn't give you neon green or a gray that looks like nothing changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tests
&lt;/h2&gt;

&lt;p&gt;Since the whole point of the extension is "don't mess up my file", the JSON editing code is tested with Node's built-in test runner, no extra framework. The tests are boring in a good way: take a settings file with comments and keys in a weird order, change the color twice, and check that the order of keys is identical and the comments are still there. Remove colors and check that only Tidy Tint's keys disappeared.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it
&lt;/h2&gt;

&lt;p&gt;Install &lt;strong&gt;Tidy Tint&lt;/strong&gt; from the Marketplace and open the command palette:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Tidy Tint: Change Color&lt;/code&gt; lets you pick from a list of favorites, type a hex value, or go random&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Tidy Tint: Random Color&lt;/code&gt; if you don't care&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Tidy Tint: Remove Colors&lt;/code&gt; to undo everything&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can turn off each bar separately (&lt;code&gt;tidyTint.affectActivityBar&lt;/code&gt;, &lt;code&gt;tidyTint.affectTitleBar&lt;/code&gt;, &lt;code&gt;tidyTint.affectStatusBar&lt;/code&gt;) and change the favorites list in &lt;code&gt;tidyTint.favoriteColors&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In a multi-root workspace the colors go in the &lt;code&gt;.code-workspace&lt;/code&gt; file, so you need to save the workspace first.&lt;/p&gt;

&lt;p&gt;One honest limitation: Tidy Tint only controls what &lt;em&gt;it&lt;/em&gt; writes. If another extension or the Settings UI later rewrites &lt;code&gt;workbench.colorCustomizations&lt;/code&gt; as a whole object, the order will change again. I can't do anything about that.&lt;/p&gt;

&lt;h2&gt;
  
  
  That's it
&lt;/h2&gt;

&lt;p&gt;It's a small extension, around 350 lines of TypeScript, and it solves a problem that maybe only bothers people who read their own diffs too carefully. If you're one of them, give it a try.&lt;/p&gt;

&lt;p&gt;The code is MIT and lives on GitHub: &lt;a href="https://github.com/cbsumastre/tidy-tint" rel="noopener noreferrer"&gt;cbsumastre/tidy-tint&lt;/a&gt;. Issues and PRs are welcome, and if you find a settings file that it mangles, I'd really like to see it.&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Cómo se calcula la letra del DNI y del NIE (módulo 23, con código)</title>
      <dc:creator>Carlos</dc:creator>
      <pubDate>Wed, 30 Sep 2026 10:39:17 +0000</pubDate>
      <link>https://dev.to/cbsumastre/como-se-calcula-la-letra-del-dni-y-del-nie-modulo-23-con-codigo-3cmj</link>
      <guid>https://dev.to/cbsumastre/como-se-calcula-la-letra-del-dni-y-del-nie-modulo-23-con-codigo-3cmj</guid>
      <description>&lt;p&gt;Si alguna vez has validado un DNI en un formulario, o has necesitado datos de prueba para un entorno de test, te habrás preguntado de dónde sale esa letra final. No es aleatoria ni un truco: es un &lt;strong&gt;dígito de control&lt;/strong&gt; que se calcula con una operación muy simple. Vamos a verlo paso a paso y con código.&lt;/p&gt;

&lt;h2&gt;
  
  
  El algoritmo del DNI
&lt;/h2&gt;

&lt;p&gt;Un DNI son &lt;strong&gt;8 dígitos y una letra&lt;/strong&gt;. El Ministerio del Interior publica el algoritmo [1], y la letra se obtiene así:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Toma los 8 dígitos como un número entero.&lt;/li&gt;
&lt;li&gt;Calcula el &lt;strong&gt;resto de dividirlo entre 23&lt;/strong&gt; (módulo 23).&lt;/li&gt;
&lt;li&gt;Busca ese resto como posición en esta tabla de 23 letras:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TRWAGMYFPDXBNJZSQVHLCKE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La posición empieza en 0. El resto 0 es &lt;code&gt;T&lt;/code&gt;, el 1 es &lt;code&gt;R&lt;/code&gt;, el 2 es &lt;code&gt;W&lt;/code&gt;, y así hasta el 22, que es &lt;code&gt;E&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ejemplo
&lt;/h3&gt;

&lt;p&gt;DNI &lt;code&gt;12345678&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;12345678 mod 23 = 14&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;La posición 14 de la tabla es &lt;code&gt;Z&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Resultado: &lt;code&gt;12345678Z&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Un caso límite del cálculo: &lt;code&gt;00000000&lt;/code&gt; da resto 0, que es &lt;code&gt;T&lt;/code&gt;, así que &lt;code&gt;00000000T&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;No están todas las letras del alfabeto: faltan &lt;code&gt;I&lt;/code&gt;, &lt;code&gt;Ñ&lt;/code&gt;, &lt;code&gt;O&lt;/code&gt; y &lt;code&gt;U&lt;/code&gt;. No hay una explicación oficial fácil de encontrar. Estas son las que circulan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;I&lt;/code&gt; y &lt;code&gt;O&lt;/code&gt;:&lt;/strong&gt; en esto coinciden todas las fuentes. Se descartan porque se confunden con &lt;code&gt;1&lt;/code&gt; y &lt;code&gt;0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Ñ&lt;/code&gt;:&lt;/strong&gt; hay dos versiones. Según el matemático Raúl Ibáñez (UPV/EHU), se quitó por los problemas con sistemas informáticos de otros países, que no tienen esa letra [3]. Según Wikipedia, para no confundirla con la &lt;code&gt;N&lt;/code&gt; [4].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;U&lt;/code&gt;:&lt;/strong&gt; según Ibáñez, para quedarse en 23 letras. El 23 es primo, y eso es lo que permite detectar errores (lo vemos más abajo) [3].&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  El NIE: el mismo algoritmo con un paso previo
&lt;/h2&gt;

&lt;p&gt;El NIE (número de identidad de extranjero) tiene una letra inicial &lt;code&gt;X&lt;/code&gt;, &lt;code&gt;Y&lt;/code&gt; o &lt;code&gt;Z&lt;/code&gt;, siete dígitos y una letra final de control.&lt;/p&gt;

&lt;p&gt;Un poco de historia: al principio el NIE era una &lt;code&gt;X&lt;/code&gt; seguida de &lt;strong&gt;ocho&lt;/strong&gt; dígitos. La Orden INT/2058/2008 [2] fijó el formato actual de nueve caracteres, con una letra inicial, siete dígitos y un carácter de verificación. También estableció que, al agotarse la serie de la &lt;code&gt;X&lt;/code&gt;, se siguiera por orden alfabético. Por eso existen los NIE con &lt;code&gt;Y&lt;/code&gt; y con &lt;code&gt;Z&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Para calcular la letra final [1]:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sustituye la letra inicial por un dígito: &lt;code&gt;X → 0&lt;/code&gt;, &lt;code&gt;Y → 1&lt;/code&gt;, &lt;code&gt;Z → 2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Concatena ese dígito con los 7 dígitos: obtienes un número de 8 cifras.&lt;/li&gt;
&lt;li&gt;Aplica el mismo módulo 23 y la misma tabla.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Ejemplo
&lt;/h3&gt;

&lt;p&gt;NIE &lt;code&gt;X1234567&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;X → 0&lt;/code&gt;, así que el número es &lt;code&gt;01234567&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1234567 mod 23 = 19&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;La posición 19 de la tabla es &lt;code&gt;L&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Resultado: &lt;code&gt;X1234567L&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Con &lt;code&gt;Y1234567&lt;/code&gt; se calcula &lt;code&gt;11234567&lt;/code&gt;, y con &lt;code&gt;Z1234567&lt;/code&gt; se calcula &lt;code&gt;21234567&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  En código (TypeScript)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LETTERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TRWAGMYFPDXBNJZSQVHLCKE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;NIE_PREFIX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;X&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;Z&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Los usuarios escriben "12.345.678-z": quita espacios, puntos y guiones.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;normalize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[\s&lt;/span&gt;&lt;span class="sr"&gt;.-&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;dniLetter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;digits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;LETTERS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;digits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isValidDni&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;(\d{8})([&lt;/span&gt;&lt;span class="sr"&gt;A-Z&lt;/span&gt;&lt;span class="se"&gt;])&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;dniLetter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isValidNie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;([&lt;/span&gt;&lt;span class="sr"&gt;XYZ&lt;/span&gt;&lt;span class="se"&gt;])(\d{7})([&lt;/span&gt;&lt;span class="sr"&gt;A-Z&lt;/span&gt;&lt;span class="se"&gt;])&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;dniLetter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;NIE_PREFIX&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;isValidDni&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;12345678Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nf"&gt;isValidDni&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;12.345.678-z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nf"&gt;isValidDni&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;12345678A&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// false&lt;/span&gt;
&lt;span class="nf"&gt;isValidNie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X1234567L&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Un detalle práctico: normaliza siempre antes de validar. Es lo que hace &lt;code&gt;normalize&lt;/code&gt;. Si no, rechazarás documentos correctos escritos con puntos, guiones o en minúsculas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué valida y qué no
&lt;/h2&gt;

&lt;p&gt;La letra detecta &lt;strong&gt;errores de tecleo&lt;/strong&gt;. Si te equivocas en un dígito o intercambias dos cifras, la letra ya no cuadra, porque ninguno de esos cambios deja el mismo resto al dividir entre 23. Lo que no hace es comprobar que el documento &lt;strong&gt;exista&lt;/strong&gt;. &lt;code&gt;12345678Z&lt;/code&gt; es un DNI con formato y letra válidos, pero eso no dice nada de si pertenece a alguien.&lt;/p&gt;

&lt;p&gt;Esto tiene una consecuencia útil para testing: puedes generar miles de DNI con letra correcta que pasan cualquier validación de formato sin corresponder a personas reales.&lt;/p&gt;

&lt;h2&gt;
  
  
  Probarlo sin escribir el código
&lt;/h2&gt;

&lt;p&gt;Si solo necesitas datos de prueba o comprobar un documento:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://datamockseed.firstflip.es/generador/dni?utm_source=devto" rel="noopener noreferrer"&gt;Generador de DNI válido&lt;/a&gt;: números aleatorios y ficticios con la letra correcta.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://datamockseed.firstflip.es/validar/dni?utm_source=devto" rel="noopener noreferrer"&gt;Validador de DNI&lt;/a&gt;: te dice si la letra cuadra y, si no, cuál debería ser.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Los datos son ficticios. Úsalos para pruebas, QA, demos o seeds de base de datos, y nunca para hacerte pasar por nadie ni en trámites reales.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resumen
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Letra del DNI = &lt;code&gt;TRWAGMYFPDXBNJZSQVHLCKE[número mod 23]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;NIE = igual, cambiando &lt;code&gt;X/Y/Z&lt;/code&gt; por &lt;code&gt;0/1/2&lt;/code&gt; al principio.&lt;/li&gt;
&lt;li&gt;Solo detecta errores de escritura: no prueba que el documento exista.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Fuentes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Ministerio del Interior. &lt;a href="https://www.interior.gob.es/opencms/es/servicios-al-ciudadano/tramites-y-gestiones/dni/calculo-del-digito-de-control-del-nif-nie/" rel="noopener noreferrer"&gt;Cálculo del dígito de control del NIF/NIE&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;BOE. &lt;a href="https://www.boe.es/buscar/doc.php?id=BOE-A-2008-12050" rel="noopener noreferrer"&gt;Orden INT/2058/2008, de 14 de julio&lt;/a&gt;, sobre el Número de Identidad de Extranjero (BOE núm. 170, de 15 de julio de 2008).&lt;/li&gt;
&lt;li&gt;Raúl Ibáñez. &lt;a href="https://culturacientifica.com/2022/01/12/un-codigo-detector-de-errores-la-letra-del-dni/" rel="noopener noreferrer"&gt;Un código detector de errores: la letra del DNI&lt;/a&gt;. Cuaderno de Cultura Científica, UPV/EHU, 2022.&lt;/li&gt;
&lt;li&gt;Wikipedia. &lt;a href="https://es.wikipedia.org/wiki/N%C3%BAmero_de_identificaci%C3%B3n_fiscal" rel="noopener noreferrer"&gt;Número de identificación fiscal&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>testing</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>spanish</category>
    </item>
  </channel>
</rss>
