<?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: Alvaro Videla</title>
    <description>The latest articles on DEV Community by Alvaro Videla (@videlalvaro).</description>
    <link>https://dev.to/videlalvaro</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%2F152413%2F2510ce21-2a15-4804-ba75-4872d5bbb545.jpeg</url>
      <title>DEV Community: Alvaro Videla</title>
      <link>https://dev.to/videlalvaro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/videlalvaro"/>
    <language>en</language>
    <item>
      <title>HTML is a programming language</title>
      <dc:creator>Alvaro Videla</dc:creator>
      <pubDate>Tue, 02 Feb 2021 21:44:47 +0000</pubDate>
      <link>https://dev.to/videlalvaro/html-is-a-programming-language-1pd4</link>
      <guid>https://dev.to/videlalvaro/html-is-a-programming-language-1pd4</guid>
      <description>&lt;p&gt;Every now and then we read debates that HTML is a programming language, that it isn't a programming language... I don't understand the point of the debate. It seems to come from a group of people that want to gatekeep who gets to be called &lt;strong&gt;a programmer&lt;/strong&gt;. In any case, having this debate without defining what we understand as programming language makes no sense. So let's look into that.&lt;/p&gt;

&lt;p&gt;The canonical source when it comes to programming language classifications is &lt;a href="https://en.wikipedia.org/wiki/Jean_E._Sammet"&gt;Jean E. Sammet&lt;/a&gt; 1969 book &lt;a href="https://www.amazon.com/Programming-Languages-Fundamentals-Automatic-Computation/dp/0137299885"&gt;Programming Languages: History and Fundamentals&lt;/a&gt;. She was an early computer pioneer, where she helped design COBOL among other programming languages. She was the first female president of the ACM so that says a lot about her career impact in computer science.&lt;/p&gt;

&lt;p&gt;In that book she provides a couple of defining characteristics for programming languages. Let's see if HTML meets them:&lt;/p&gt;

&lt;h2&gt;
  
  
  Machine Knowledge Is Unnecessary
&lt;/h2&gt;

&lt;p&gt;In this case "a programming language requires no knowledge of the machine code by the user". The user shouldn't learn about registers, how numbers are represented by the machine, and so on. This doesn't not mean, as Sammet clarifies, that the programmer cannot learn about these characteristic of the machine in order to write better programs, is that it is not required to know them to use the language. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;HTML meets this characteristic&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Potential for Conversion to Other Computers
&lt;/h2&gt;

&lt;p&gt;From the previous characteristic we can derive the second one. If the user can ignore the machine in which the program is going to run, a programming language thus must be able to be ported to different machines. Programming languages should be machine independent as Sammet says.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;HTML meets this characteristic&lt;/em&gt;. It runs in every browser in most modern computers out there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Instruction Explosion
&lt;/h2&gt;

&lt;p&gt;In this case when the program is translated to machine code, each statement in the program should produce multiple machine code instructions. Also says Sammet, the user doesn't need to write any of this machine code for their program to work.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;HTML meets this characteristic&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem-Oriented Notation
&lt;/h2&gt;

&lt;p&gt;"A programming language should have a notation which is somewhat closer to the specific problem being solved than is normal machine code". So instead of doing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLA C
MPY D
ADD B
STO A
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A problem oriented language would have a syntax 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;A = B + C * D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;HTML meets this characteristic&lt;/em&gt;. It's pretty clear that HTML fits this requirement. &lt;/p&gt;

&lt;p&gt;So based on her selection criteria I would assume that HTML would have made it into her book mentioned above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where is Turing Completeness?
&lt;/h2&gt;

&lt;p&gt;As you can see, in Sammet requirements for programming languages, Turing completeness is not part of them. Even more, Alan Turing is not cited in the book, among the hundreds of references cited there, not one of the was Turing. Which makes us wonder what was the influence his ideas had back in the 60s when we had the programming language explosion.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>html</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>Programming Advice From 1976</title>
      <dc:creator>Alvaro Videla</dc:creator>
      <pubDate>Mon, 18 Jan 2021 23:05:35 +0000</pubDate>
      <link>https://dev.to/videlalvaro/programming-advice-from-1976-2j65</link>
      <guid>https://dev.to/videlalvaro/programming-advice-from-1976-2j65</guid>
      <description>&lt;p&gt;This week landed in my inbox the book Software Reliability - Principles and Practices, by Glenford J. Myers from the IBM Systems Research Institute. It's a great book with tons of advice that's very relevant today, even though it was published over forty years ago.&lt;/p&gt;

&lt;p&gt;The topic being Software Reliability, one might think that the whole book talks about fault tolerance techniques, algorithms, and so on. While that's part of the book, it's not its main theme. The book focuses on the human aspect of programming, and how making improvements there results in software that's more reliable, because it has less bugs, it's easier to maintain, it easier to use for final users, and so on.&lt;/p&gt;

&lt;p&gt;In a section about programming clarity we find the following quote:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The programmer’s goal must be writing source code for the primary audience of people instead of machines.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Code that's easier for other humans to understand and read. So when it comes to project maintenance, people can tell what a program is doing. When everything is on fire due to a bug in production, nobody will care how fast that code was written, but people will care if they understand the code, and they are able to fix the issue.&lt;/p&gt;

&lt;p&gt;Even in 1976, the idea of writing code for humans to read, wasn't new. In the 1967 Datamation issue there was an article called &lt;a href="http://archive.computerhistory.org/resources/text/Knuth_Don_X4100/PDF_index/k-9-pdf/k-9-u2769-1-Baker-What-Programmer-Does.pdf"&gt;What A Programmer Does&lt;/a&gt;. One of the best &lt;em&gt;unknown&lt;/em&gt; articles about best programming practices out there. The author says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A programmer does not primarily write code; rather, he primarily writes to another programmer about his problem solution. The understanding of this fact is the final step in his maturation as a technician.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A program is a way to instruct a computer how to perform a task for a certain problem, but at the same time, that text is a document that tells other programmers how that problem was solved. How important it is to write readable code! We must write code with our peers in mind, and let the compiler worry of warning us when the computer won't understand something.&lt;/p&gt;

&lt;p&gt;The question is how do we make code that's easier to understand? To start there's a lot of advice out there about how to write clean code, or as Myers calls it in his book "Programming Style". He mentions as inspiration the excellent book &lt;a href="https://en.wikipedia.org/wiki/The_Elements_of_Programming_Style"&gt;The Elements of Programming Style&lt;/a&gt; by Kernighan and Plauger. The section provides guidelines like "Use meaningful variable names", or "Do not alter the value of the iteration variable within a loop". While I think these are all good advice, I also think we should dig a bit deeper on what it means to write code that's easier to understand.&lt;/p&gt;

&lt;p&gt;To that end, I've been reading about literary theory and seeing how to bring learnings from that field into programming. While I won't repeat myself here, I'll link you to the article &lt;a href="http://alvaro-videla.com/2018/05/lector-in-codigo.html"&gt;Lector in Codigo&lt;/a&gt; which I wrote as a first introduction to this topic. Recently I wrote an &lt;a href="https://dev.to/videlalvaro/the-code-speaks-on-its-own-not-3jf9"&gt;article&lt;/a&gt; applying these ideas while reading &lt;a href="https://code.visualstudio.com/?WT.mc_id=devto-blog-alvidela"&gt;Visual Studio Code&lt;/a&gt; own source code.&lt;/p&gt;

&lt;p&gt;This area has a lot to explore, so expect more articles bridging these two disciplines together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Appendix
&lt;/h2&gt;

&lt;p&gt;The idea of programming for other humans to read has appeared many times. Here's a few more instances:&lt;/p&gt;

&lt;p&gt;Knuth talks about it in his 1983 &lt;a href="https://tex.loria.fr/litte/knuthweb.pdf"&gt;Literate Programming&lt;/a&gt; paper:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Instead of imagining that our main task is to instruct a computer what to do, let us concentrate rather on explaining to human beings what we want a computer to do.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Of course this idea kept coming back. It's part of the 1985 book &lt;a href="https://mitpress.mit.edu/sites/default/files/sicp/index.html"&gt;Structure and Interpretation of Computer Programs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Thus, programs must be written for people to read, and only incidentally for machines to execute. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Finally in the book &lt;a href="https://10print.org/"&gt;10 PRINT CHR$(205.5+RND(1)); : GOTO 10&lt;/a&gt; Manfort et al. analyze the line of code from the book title, saying that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;the presence of these optional spaces indicates some concern for the people who will deal with this code, rather than merely the machine that will process it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This idea keeps coming over and over again. We programmers don't seem to learn.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>cleancode</category>
      <category>bestpractices</category>
    </item>
    <item>
      <title>How to upload images to Blob Storage using Serverless and Static Web Apps</title>
      <dc:creator>Alvaro Videla</dc:creator>
      <pubDate>Thu, 17 Sep 2020 15:31:09 +0000</pubDate>
      <link>https://dev.to/azure/how-to-upload-images-to-blob-storage-using-serverless-and-static-web-apps-ep1</link>
      <guid>https://dev.to/azure/how-to-upload-images-to-blob-storage-using-serverless-and-static-web-apps-ep1</guid>
      <description>&lt;p&gt;If you have an app that's accessed publicly via the browser, you want to restrict who's able to upload images to your storage backend, but by going the way of &lt;a href="https://docs.microsoft.com/azure/static-web-apps/?WT.mc_id=servsept20-blog-alvidela" rel="noopener noreferrer"&gt;Azure Static Web Apps&lt;/a&gt;, you are presented with the problem of how to authenticate users towards &lt;a href="https://docs.microsoft.com/azure/storage/blobs/?WT.mc_id=servsept20-blog-alvidela" rel="noopener noreferrer"&gt;Azure Blob Storage&lt;/a&gt;. Luckily, there's a solution for that. Add an &lt;a href="https://docs.microsoft.com/azure/azure-functions/?WT.mc_id=servsept20-blog-alvidela" rel="noopener noreferrer"&gt;Azure Functions&lt;/a&gt; backend that takes care of generating &lt;a href="https://docs.microsoft.com/azure/storage/common/storage-sas-overview?WT.mc_id=servsept20-blog-alvidela" rel="noopener noreferrer"&gt;SAS Keys&lt;/a&gt; so your users can upload pictures directly to Azure Blob Storage without needing to create an account inside our systems.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can access the source code for this example here on GitHub: &lt;a href="https://github.com/videlalvaro/upload_image" rel="noopener noreferrer"&gt;videlalvaro/upload_image&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  ServerlessSeptember
&lt;/h2&gt;

&lt;p&gt;This article is part of &lt;a href="https://aka.ms/ServerlessSeptember2020" rel="noopener noreferrer"&gt;#ServerlessSeptember&lt;/a&gt;. You'll find other helpful articles, detailed tutorials, and videos in this all-things-Serverless content collection. New articles from community members and cloud advocates are published every week from Monday to Thursday through September. &lt;/p&gt;

&lt;p&gt;Find out more about how Microsoft Azure enables your Serverless functions at &lt;a href="https://docs.microsoft.com/azure/azure-functions/?WT.mc_id=servsept20-devto-cxaall" rel="noopener noreferrer"&gt;https://docs.microsoft.com/azure/azure-functions/&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we are building
&lt;/h2&gt;

&lt;p&gt;Here are the steps of what you need to do to accomplish that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Setup Azure Blob Storage&lt;/li&gt;
&lt;li&gt;Create an Azure Functions API for your frontend&lt;/li&gt;
&lt;li&gt;Create the HTML/JS/CSS frontend for your app&lt;/li&gt;
&lt;li&gt;Learn how to run your app in Visual Studio Code&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;To complete this tutorial you will need an Azure Account. &lt;a href="https://azure.microsoft.com/free/?WT.mc_id=servsept20-blog-alvidela" rel="noopener noreferrer"&gt;Follow this link to signup for free&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Setting up Azure Blob Storage
&lt;/h2&gt;

&lt;p&gt;Once you have registered your account on Azure, log in and create an &lt;a href="https://docs.microsoft.com/azure/storage/common/storage-account-create?tabs=azure-portal&amp;amp;WT.mc_id=servsept20-blog-alvidela" rel="noopener noreferrer"&gt;Azure Storage Account&lt;/a&gt; called &lt;code&gt;uploadimagesample&lt;/code&gt; (feel free to use any other name you prefer). You can do that by clicking the big plus button that says "Create a new resource", and then type "Storage Account" in the "Search the Marketplace" bar.&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%2Ffltwmyth7q4vfl7j5mh3.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%2Ffltwmyth7q4vfl7j5mh3.png" alt="Storage Account"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a container
&lt;/h3&gt;

&lt;p&gt;Then navigate to your new storage account, select &lt;em&gt;Containers&lt;/em&gt; below, and create a new container called &lt;em&gt;images&lt;/em&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%2Fz82yb4ht4qmw9jl8cv6j.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%2Fz82yb4ht4qmw9jl8cv6j.png" alt="New Container Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.microsoft.com/azure/storage/blobs/storage-quickstart-blobs-portal?WT.mc_id=servsept20-blog-alvidela" rel="noopener noreferrer"&gt;Follow this link to learn more on how to create a container&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Setup CORS
&lt;/h3&gt;

&lt;p&gt;Now it's time to set up &lt;a href="https://docs.microsoft.com/rest/api/storageservices/cross-origin-resource-sharing--cors--support-for-the-azure-storage-services?WT.mc_id=servsept20-blog-alvidela" rel="noopener noreferrer"&gt;CORS&lt;/a&gt; for your storage account. This will allow your app to send data from your own domain to Azure via HTTP, and circumvent the &lt;a href="https://www.w3.org/Security/wiki/Same_Origin_Policy" rel="noopener noreferrer"&gt;same-origin&lt;/a&gt; policy from browsers.&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%2Fle35al56z560agfy6d7s.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%2Fle35al56z560agfy6d7s.png" alt="CORS Setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see from the image, you need to setup a &lt;code&gt;*&lt;/code&gt; for Allowed origins, Allowed headers, and Exposed headers. Also select the HTTP verbs that you want to allow, and leave the Max age value as is. If you want later you can customize these values to fit your needs.&lt;/p&gt;

&lt;p&gt;Now that you have set up Azure Blob Storage for image upload, it's time to create your Azure Functions API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Serverless backend
&lt;/h2&gt;

&lt;p&gt;For a client to be able to use anonymous authentication when sending data to Azure Blob Storage they would need to have a &lt;a href="https://docs.microsoft.com/azure/storage/common/storage-sas-overview?WT.mc_id=servsept20-blog-alvidela" rel="noopener noreferrer"&gt;SAS key&lt;/a&gt; allowing them to perform their requests. You are going to create a serverless API that creates such a key, and sends it to the browser.&lt;/p&gt;

&lt;p&gt;Create a new folder for the project called &lt;code&gt;upload_image&lt;/code&gt;, and then open that folder in Visual Studio Code. Then press &lt;code&gt;F1&lt;/code&gt; and select &lt;em&gt;Azure Functions: Create New Project&lt;/em&gt;. Choose &lt;em&gt;JavaScript&lt;/em&gt; as the programming language, and finally &lt;em&gt;HTTP trigger&lt;/em&gt; as the template for your new serverless function. The name for the function will be &lt;em&gt;credentials&lt;/em&gt;, and the Authorization level &lt;em&gt;Anonymous&lt;/em&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you haven't done so, you need to install &lt;a href="https://docs.microsoft.com/azure/azure-functions/functions-run-local?WT.mc_id=servsept20-blog-alvidela" rel="noopener noreferrer"&gt;Azure Functions Core Tools&lt;/a&gt; to run the project locally.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Configure your storage connection
&lt;/h3&gt;

&lt;p&gt;The last step to configure Azure Blob Storage is to tell Visual Studio Code how to connect to your storage account. For that go to Azure Portal and open the Access Keys section in your storage account. Grab the &lt;em&gt;Connection String&lt;/em&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%2Fz8ujr3aanxy264flwcu8.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%2Fz8ujr3aanxy264flwcu8.png" alt="Connection String"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open the file called &lt;code&gt;local.settings.json&lt;/code&gt; at the root of your project. There, edit the &lt;code&gt;AzureWebJobsStorage&lt;/code&gt; key to include the storage connection string you just obtained from Azure Portal. See picture above. Keep in mind this information is private, so do not commit this file to git!&lt;/p&gt;

&lt;p&gt;It should look like this, but with your actual connection string:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"IsEncrypted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Values"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"AzureWebJobsStorage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DefaultEndpointsProtocol=https;AccountName=youraccountname;AccountKey=&amp;lt;SecretAccountKey&amp;gt;;EndpointSuffix=core.windows.net"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"FUNCTIONS_WORKER_RUNTIME"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;Now it's time to implement your serverless function.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to generate a SAS Key with Serverless
&lt;/h3&gt;

&lt;p&gt;To generate a SAS key that can be used to authenticate to Azure anonymously, you need to install the &lt;a href="https://docs.microsoft.com/azure/developer/javascript/?view=azure-node-latest&amp;amp;WT.mc_id=servsept20-blog-alvidela" rel="noopener noreferrer"&gt;Azure SDK for blob storage&lt;/a&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

 npm &lt;span class="nb"&gt;install&lt;/span&gt; @azure/storage-blob


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

&lt;/div&gt;

&lt;p&gt;From the &lt;code&gt;storage-blob&lt;/code&gt; SDK we are going to use the function &lt;code&gt;generateBlobSASQueryParameters&lt;/code&gt; that creates a query string with the right authentication info that will let a client upload images to storage. That function requires a &lt;code&gt;containerName&lt;/code&gt;, a set of &lt;code&gt;permissions&lt;/code&gt; like &lt;em&gt;read&lt;/em&gt;, &lt;em&gt;write&lt;/em&gt;, etc., an &lt;code&gt;expiresOn&lt;/code&gt; parameter for the SAS key, and a &lt;code&gt;StorageSharedKeyCredential&lt;/code&gt; with the authentication info from your connection string. You are going to implement a function called &lt;code&gt;generateSasToken&lt;/code&gt; that will take care of that process.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;index.js&lt;/code&gt; file from your &lt;code&gt;credentials&lt;/code&gt; folder and add the following function at the bottom:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateSasToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;accountKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;accountName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;extractConnectionStringParts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;connectionString&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;sharedKeyCredential&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StorageSharedKeyCredential&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;accountKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;expiryDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;expiryDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expiryDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHours&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&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;sasKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateBlobSASQueryParameters&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;containerName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ContainerSASPermissions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;expiresOn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;expiryDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;sharedKeyCredential&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;sasKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sasKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;url&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 function &lt;code&gt;generateSasToken&lt;/code&gt; takes a &lt;code&gt;connectionString&lt;/code&gt; like the one you just copied into &lt;code&gt;local.settings.json&lt;/code&gt; and parses it by calling the &lt;code&gt;extractConnectionStringParts&lt;/code&gt; function to extract values like &lt;code&gt;AccountKey&lt;/code&gt; or &lt;code&gt;AccountName&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then we create a &lt;code&gt;StorageSharedKeyCredential&lt;/code&gt; by providing the &lt;code&gt;accountName&lt;/code&gt; and &lt;code&gt;accountKey&lt;/code&gt; you just extracted. In the case of the &lt;code&gt;accountKey&lt;/code&gt;, you need to convert it to string using the &lt;code&gt;base64&lt;/code&gt; encoding, because it comes out as a &lt;code&gt;Buffer&lt;/code&gt; from the parser function.&lt;/p&gt;

&lt;p&gt;Next you need to set an expiry date for the generated key. So you can create a &lt;code&gt;Date&lt;/code&gt; object and then set its time to two hours in the future. You can change the expiry time to adapt to your use case.&lt;/p&gt;

&lt;p&gt;With everything in place you can call &lt;code&gt;generateBlobSASQueryParameters&lt;/code&gt; from the &lt;code&gt;@azure/storage-blob&lt;/code&gt; SDK and obtain the sasKey. Finally the return value of the function is the query string that includes our sasKey, and the URL that points to our storage instance.&lt;/p&gt;

&lt;p&gt;Now it's time to implement the serverless function that will send the results from &lt;code&gt;generateSasToken&lt;/code&gt; to the client. As you can see the function is quite basic:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&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;permissions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;c&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;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;images&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;generateSasToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AzureWebJobsStorage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;done&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;Here you can specify the storage permissions you are giving to the users, in this case just &lt;code&gt;c&lt;/code&gt; that stands for &lt;em&gt;create&lt;/em&gt; permissions. Then the container is called &lt;code&gt;images&lt;/code&gt;, like the one you created above. From the &lt;code&gt;process.env.AzureWebJobsStorage&lt;/code&gt; environment variable you can obtain the value that you set up in your &lt;code&gt;local.settings.json&lt;/code&gt; file.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When you deploy this function to Azure, you need to setup the &lt;code&gt;AzureWebJobsStorage&lt;/code&gt; variable in your &lt;a href="https://docs.microsoft.com/azure/azure-functions/functions-how-to-use-azure-function-app-settings?WT.mc_id=servsept20-blog-alvidela" rel="noopener noreferrer"&gt;app settings&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Take a look at the final &lt;a href="https://github.com/videlalvaro/upload_image/blob/master/credentials/index.js" rel="noopener noreferrer"&gt;&lt;code&gt;index.js&lt;/code&gt; file&lt;/a&gt; in the repo to find the required imports for your serverless functions, and also to find the &lt;code&gt;utils.js&lt;/code&gt; module that includes the &lt;code&gt;extractConnectionStringParts&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;The next step is to implement the frontend part to contact your serverless API and upload the image to Azure Blob Storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the Static Web App Frontend
&lt;/h2&gt;

&lt;p&gt;Start by creating an &lt;code&gt;index.html&lt;/code&gt; file in the root folder, and add the following code to it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Azure Blob Storage Image Upload&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/bulma@0.9.0/css/bulma.min.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"section"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Loading SASKey from the API: &lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;pre&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"image"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Choose a profile picture:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"image"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"image"&lt;/span&gt; &lt;span class="na"&gt;accept=&lt;/span&gt;&lt;span class="s"&gt;"image/png, image/jpeg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"./dist/main.js"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sasKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/credentials&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`SAS Key: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sasKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;`URL: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;images&lt;/span&gt;&lt;span class="dl"&gt;'&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;files&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="nf"&gt;blobUpload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;images&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sasKey&lt;/span&gt;&lt;span class="p"&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;fileInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;fileInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;uploadFile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}())&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Let's focus our attention on that &lt;code&gt;&amp;lt;script /&amp;gt;&lt;/code&gt; segment. There you have an async function that will query the serverless API by calling &lt;code&gt;fetch("/api/credentials")&lt;/code&gt;. That call will get you the &lt;code&gt;url&lt;/code&gt; and &lt;code&gt;sasKey&lt;/code&gt; values you generated earlier in the serverless function.&lt;/p&gt;

&lt;p&gt;Then whenever the user selects a file, the &lt;code&gt;change&lt;/code&gt; event from the file selector will fire, calling the &lt;code&gt;uploadFile&lt;/code&gt; function. There we get the file information and pass it to the &lt;code&gt;blobUpload&lt;/code&gt; function, so the file is uploaded to Azure Blob Storage. The function accepts the file object, a target URL, a container name, and SAS key.&lt;/p&gt;

&lt;p&gt;To implement the &lt;code&gt;blobUpload&lt;/code&gt; function, create a &lt;code&gt;src&lt;/code&gt; folder and add an index.js file there. Then insert the following code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BlockBlobClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;AnonymousCredential&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@azure/storage-blob&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;blobUpload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sasKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;blobName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildBlobName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;blobName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sasKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;blockBlobClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BlockBlobClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AnonymousCredential&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nx"&gt;blockBlobClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uploadBrowserData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildBlobName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&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;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lastIndexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lastIndexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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;return&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&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;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;ext&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 Azure Blob Storage Javascript SDK provides a &lt;code&gt;BlockBlobClient&lt;/code&gt; class that comes with a &lt;code&gt;uploadBrowserData&lt;/code&gt; method. You are going to use that to upload images to Azure Blob Storage.&lt;/p&gt;

&lt;p&gt;To create a &lt;code&gt;BlockBlobClient&lt;/code&gt; you'll need the login information, which consists of the URL including the query string that contains your SAS Key, and an &lt;code&gt;AnonymousCredential&lt;/code&gt; instance to tell the &lt;code&gt;BlockBlobClient&lt;/code&gt; how to authenticate to Azure.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;login&lt;/code&gt; information has the following format: &lt;code&gt;${url}/${container}/${blobName}?${sasKey}&lt;/code&gt;. &lt;code&gt;url&lt;/code&gt; and &lt;code&gt;sasKey&lt;/code&gt; was the data you got from the serverless function call. &lt;code&gt;blobName&lt;/code&gt; is a randomly generated name for the uploaded image obtained by calling &lt;code&gt;buildBlobName&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now there's a &lt;strong&gt;very important detail&lt;/strong&gt; in the &lt;code&gt;require&lt;/code&gt; at the top of the file. You are requiring a &lt;code&gt;node.js&lt;/code&gt; module in JavaScript code that will run in the frontend. For that to work you need to use Webpack to do the proper transformation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the Azure Blob Storage SDK with Webpack
&lt;/h3&gt;

&lt;p&gt;Install Webpack by running the following command in your projects root folder:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npm &lt;span class="nb"&gt;install &lt;/span&gt;webpack &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;webpack-cli &lt;span class="nt"&gt;--save-dev&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Then run webpack by typing:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

 webpack &lt;span class="nt"&gt;--mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;development


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

&lt;/div&gt;

&lt;p&gt;That command will extract the relevant files from the &lt;code&gt;@azure/storage-blob&lt;/code&gt; SDK and make them compatible with the browser execution environment. The generated files will live in the &lt;code&gt;dist&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;Now you are ready to test the app and start uploading images to Azure Blob Storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the app
&lt;/h2&gt;

&lt;p&gt;Let's start by running the Azure Functions backend. Pressing &lt;code&gt;F5&lt;/code&gt; in Visual Studio Code should do the. You should see 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%2Ffxq6t5ot9u99bwbqchl6.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%2Ffxq6t5ot9u99bwbqchl6.png" alt="Azure Functions Running CLI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To run the Static Web App locally you need to install the Live Server extension for visual studio code. Once it's installed, then press &lt;code&gt;F1&lt;/code&gt; and enter &lt;code&gt;Open with Live Server&lt;/code&gt;. This will open a browser tab with the project running there:&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%2Fve02a1j1x2n8k7tgq8sf.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%2Fve02a1j1x2n8k7tgq8sf.png" alt="Upload Form"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select an image from your computer and upload it to Azure Blob Storage. If all went well, we should see the image in the Storage Explorer:&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%2Fwz6h0uvsisne7wkjgjfk.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%2Fwz6h0uvsisne7wkjgjfk.png" alt="Storage Explorer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congrats! You just uploaded an image from an Azure Static Web App using Azure Functions to generate the SAS Key!&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Learn more about &lt;a href="https://docs.microsoft.com/azure/azure-functions/?WT.mc_id=servsept20-blog-alvidela" rel="noopener noreferrer"&gt;Azure Functions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Learn More about &lt;a href="https://docs.microsoft.com/azure/static-web-apps/?WT.mc_id=servsept20-blog-alvidela" rel="noopener noreferrer"&gt;Azure Static Web Apps&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Learn more about &lt;a href="https://docs.microsoft.com/azure/storage/blobs/?WT.mc_id=servsept20-blog-alvidela" rel="noopener noreferrer"&gt;Azure Blob Storage&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;You can access the source code for this example here on GitHub: &lt;a href="https://github.com/videlalvaro/upload_image" rel="noopener noreferrer"&gt;videlalvaro/upload_image&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>serverless</category>
      <category>tutorial</category>
      <category>javascript</category>
      <category>azure</category>
    </item>
    <item>
      <title>Azure Functions with WSL2 and Visual Studio Code</title>
      <dc:creator>Alvaro Videla</dc:creator>
      <pubDate>Thu, 13 Aug 2020 23:47:49 +0000</pubDate>
      <link>https://dev.to/videlalvaro/azure-functions-with-wsl2-and-visual-studio-code-56c1</link>
      <guid>https://dev.to/videlalvaro/azure-functions-with-wsl2-and-visual-studio-code-56c1</guid>
      <description>&lt;p&gt;When executing &lt;a href="https://code.visualstudio.com/?WT.mc_id=devto-blog-alvidela" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt; through &lt;a href="https://docs.microsoft.com/windows/wsl/about?WT.mc_id=devto-blog-alvidela" rel="noopener noreferrer"&gt;WSL2&lt;/a&gt; we might find some problems when running &lt;a href="https://docs.microsoft.com/azure/azure-functions/?WT.mc_id=devto-blog-alvidela" rel="noopener noreferrer"&gt;Azure Functions&lt;/a&gt; locally. While those problems are easy to fix, they might leave you puzzled trying to find the solutions for them. In this article we'll look into that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure Functions Core Tools are not installed
&lt;/h2&gt;

&lt;p&gt;Once you have an Azure Functions project on Visual Studio Code, you might want to run it locally for debugging it by pressing &lt;code&gt;F5&lt;/code&gt;. Here's an error you might encounter:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You must have Azure Functions Core Tools installed to debug your local functions&lt;/p&gt;
&lt;/blockquote&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%2Fy8dl5wyb8ova3d7919n4.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%2Fy8dl5wyb8ova3d7919n4.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So the problem here is clearly that we don't have &lt;a href="https://docs.microsoft.com/azure/azure-functions/functions-run-local?WT.mc_id=devto-blog-alvidela" rel="noopener noreferrer"&gt;Azure Functions Core Tools&lt;/a&gt;, now the question is where to install it. &lt;/p&gt;

&lt;p&gt;Since we are running Visual Studio Code from WSL2 we need to install the Azure Functions Core Tools on the WSL2 side. Installing them on Windows 10 won't solve this issue.&lt;/p&gt;

&lt;p&gt;Here's how to install them on Ubuntu 18.04:&lt;/p&gt;

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

wget -q https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb


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

&lt;/div&gt;

&lt;p&gt;For a full set of installation instructions take a look to this article &lt;a href="https://docs.microsoft.com/azure/azure-functions/functions-run-local?WT.mc_id=devto-blog-alvidela" rel="noopener noreferrer"&gt;Work with Azure Functions Core Tools&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Could not connect to debug target
&lt;/h2&gt;

&lt;p&gt;The second problem I've found was Visual Studio Code telling me it wasn't able to attach the debugger:&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%2Ft6lv15vo2m6xiqfa8zln.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%2Ft6lv15vo2m6xiqfa8zln.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this case my problem was that while I did have node.js installed, I didn't have the supported version which is v12. Here's how to solve that problem:&lt;/p&gt;

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

npm install -g n
n lts


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

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;n&lt;/code&gt; is a program that you can use to install various versions of node.js on your system. As of this writing the LTS version (12.18.3) is the one we want, so by running &lt;code&gt;n lts&lt;/code&gt; we can get that version running on our system.&lt;/p&gt;

&lt;p&gt;And that's it! Two very quick solutions for problems you might find when running Azure Functions locally on WSL2 on Windows 10. Until next time!&lt;/p&gt;

</description>
      <category>azure</category>
      <category>serverless</category>
      <category>vscode</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to bridge RabbitMQ with Service Bus</title>
      <dc:creator>Alvaro Videla</dc:creator>
      <pubDate>Mon, 22 Jun 2020 23:43:48 +0000</pubDate>
      <link>https://dev.to/azure/how-to-bridge-rabbitmq-with-azure-service-bus-98l</link>
      <guid>https://dev.to/azure/how-to-bridge-rabbitmq-with-azure-service-bus-98l</guid>
      <description>&lt;p&gt;In this article we are going to learn how to send messages from RabbitMQ to Azure Service Bus. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WXm6yRQ4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/z5t8lfqaqzjof414kbml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WXm6yRQ4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/z5t8lfqaqzjof414kbml.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's a few scenarios in which we can make use of these capabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Edge Setups&lt;/strong&gt;: We have an edge setup where we are sending messages to RabbitMQ, but we want to forward those messages to &lt;a href="https://docs.microsoft.com/azure/service-bus-messaging/service-bus-messaging-overview?WT.mc_id=devto-blog-alvidela"&gt;Azure Service Bus&lt;/a&gt; for further processing, so we can use many of the &lt;a href="https://docs.microsoft.com/azure/architecture/guide/architecture-styles/big-data?WT.mc_id=devto-blog-alvidela"&gt;Azure Big Data capabilities&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hybrid Cloud&lt;/strong&gt;: Your company just acquired a third party that uses RabbitMQ for their messaging needs. They are on a different cloud. While they transition to Azure you can already start sharing data by bridging RabbitMQ with Service Bus.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Third Party Integration&lt;/strong&gt;: A third party uses RabbitMQ as a broker, and wants to send their data to us, but they are outside our organization. We can provide them with SAS Key giving them access to a limited set of Service Bus queues where they can forward their messages to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The list goes on, but we can solve most of these use cases by bridging RabbitMQ to Azure.&lt;/p&gt;

&lt;p&gt;First you need to create a free Azure account by signing up &lt;a href="https://azure.microsoft.com/free/?WT.mc_id=devto-blog-alvidela"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you are signed in to your account go to the azure &lt;a href="https://portal.azure.com/"&gt;portal&lt;/a&gt; and create a new Service Bus &lt;a href="https://docs.microsoft.com/azure/service-bus-messaging/service-bus-create-namespace-portal?WT.mc_id=devto-blog-alvidela"&gt;namespace&lt;/a&gt;. Namespaces are the scoping containers where our messaging components will live, like queues and topics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a new Service Bus Namespace
&lt;/h2&gt;

&lt;p&gt;In Azure Portal click the big plus button to add a new resource&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AgFAjtox--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cmgjowwe1o9kf691lejz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AgFAjtox--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cmgjowwe1o9kf691lejz.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then select Integration and click on Service Bus to create a messaging namespace:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PVMVomZf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2ipzjx3zzxu1odsyb9px.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PVMVomZf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2ipzjx3zzxu1odsyb9px.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will be prompted to enter the information for the namespace. So first select the Azure subscription you want to use, and create a new resource group if you don't have one already (more on resource groups &lt;a href="https://docs.microsoft.com/azure/azure-resource-manager/management/manage-resource-groups-portal?WT.mc_id=devto-blog-alvidela"&gt;here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ko4axlSY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/10we4barufpnj139zken.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ko4axlSY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/10we4barufpnj139zken.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;Namespace name&lt;/code&gt; you can use &lt;code&gt;rabbitmq&lt;/code&gt; but it could be anything you want. Then set &lt;code&gt;East US&lt;/code&gt; for the location, and in this case take the &lt;code&gt;Basic&lt;/code&gt; price tier.&lt;/p&gt;

&lt;p&gt;If all went well you should see the following confirmation screen:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GOvxIllE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/myd5upz1nrou900wk36c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GOvxIllE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/myd5upz1nrou900wk36c.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then back at the Azure Portal you'll see your new &lt;code&gt;rabbitmq&lt;/code&gt; namespace listed there. Click on it to access the resource so you can add a queue to it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bP4biTrI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kylm53n5m4ch20v3i13z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bP4biTrI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kylm53n5m4ch20v3i13z.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating our Service Bus Queue
&lt;/h2&gt;

&lt;p&gt;Now that you have your service bus namespace, click on the &lt;code&gt;Queues&lt;/code&gt; button on the left, under &lt;code&gt;Entities&lt;/code&gt;, so you can add a new queue:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--syxPt0Pq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kzen18ehw875fifaxf76.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--syxPt0Pq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kzen18ehw875fifaxf76.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The name of the queue will be &lt;code&gt;from-rabbitmq&lt;/code&gt; just as a reminder to where are the messages coming from. You can leave all the other options as defaults, but of course you can change them to fit the needs of your app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enabling the RabbitMQ Shovel Plugin
&lt;/h2&gt;

&lt;p&gt;To ship messages from RabbitMQ to Azure Service Bus we are going to use the &lt;a href="https://www.rabbitmq.com/shovel.html"&gt;Shovel Plugin&lt;/a&gt; that comes packaged with RabbitMQ. You can enable it with the following command (you might need to run this command with administrator privileges or via the rabbitmq user):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;rabbitmq-plugins &lt;span class="nb"&gt;enable &lt;/span&gt;rabbitmq_shovel_management
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Actually that command enables the visual interface for the plugin, which ends up enabling the shovel plugin for us.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now is time to get the credentials required for connecting RabbitMQ to Azure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting RabbitMQ to Azure Service Bus
&lt;/h2&gt;

&lt;p&gt;You'll need to create a &lt;a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-sas-overview?WT.mc_id=devto-blog-alvidela"&gt;Shared Access Policy&lt;/a&gt; (SAS) for your queue, so RabbitMQ can publish messages to it. A SAS Policy will allow us to specify how much an external party can do with our resource. In this case the idea is that RabbitMQ is just able to send messages, but not listen to the queue, or to manage the queue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2cmks9fT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rnuz2716uoc6ei2vi6lg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2cmks9fT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rnuz2716uoc6ei2vi6lg.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So in this case tick the &lt;code&gt;Send&lt;/code&gt; box and then click &lt;code&gt;Create&lt;/code&gt; to have our SAS Policy in place.&lt;/p&gt;

&lt;p&gt;Once the policy has been created click on it to see the &lt;strong&gt;Primary Connection String&lt;/strong&gt; which is what you are going to use to let RabbitMQ talk to Azure Service Bus:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Jjl_Xfp0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/revcrtlfzmoenoxoi37o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Jjl_Xfp0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/revcrtlfzmoenoxoi37o.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before you can use that connection string, you'll need to convert it to RabbitMQ's AMQP connection format. I created a basic website to ease that process. So go &lt;a href="https://red-mushroom-0f7446a0f.azurestaticapps.net/"&gt;here&lt;/a&gt; and paste your connection string in the form, click convert, and you will get a connection string that is RabbitMQ ready. (Don't worry, that website runs everything in your browser so your data is safe). If you wanna be sure, the source code is on &lt;a href="https://github.com/videlalvaro/connstring_to_amqp"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--grcHPsKl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/diyn24ql63wfatlehck4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--grcHPsKl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/diyn24ql63wfatlehck4.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now open the RabbitMQ management plugin in our browsers &lt;a href="http://localhost:15672/#/dynamic-shovels"&gt;http://localhost:15672/#/dynamic-shovels&lt;/a&gt; and go to &lt;code&gt;Admin -&amp;gt; Shovel Management&lt;/code&gt;, where you can add your new shovel that will take care of sending messages from a RabbitMQ queue to your Azure Service Bus queue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FfMh6OME--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cjpbx5o6kef7985ngiji.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FfMh6OME--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cjpbx5o6kef7985ngiji.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here name your shovel &lt;code&gt;azure&lt;/code&gt; and choose &lt;code&gt;AMQP 0.9.1&lt;/code&gt; as the source protocol. This tells the shovel plugin how to connect to your local RabbitMQ server. In the screenshot we have &lt;code&gt;amqp://&lt;/code&gt; which is the default URI that connects us to a local RabbitMQ server. Make sure to adapt that to your current deployment. &lt;/p&gt;

&lt;p&gt;On the queue side of things, specify the name of your queue. In this case I used &lt;code&gt;azure&lt;/code&gt; which is the name of a queue that I had already created in RabbitMQ. If you don't have that queue you can create it on the RabbitMQ management plugin, under the &lt;strong&gt;Queues&lt;/strong&gt; tab. You can leave the other options as default.&lt;/p&gt;

&lt;p&gt;Then on the &lt;code&gt;destination&lt;/code&gt; side of things, we are going to choose &lt;code&gt;AMQP 1.0&lt;/code&gt; as the protocol. In the &lt;code&gt;URI&lt;/code&gt; field we'll enter the connecting string that we got from the previous step that converted our Azure connection string to our RabbitMQ format. It should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amqps://rabbitmq-shovel:StringOfRandomChars@rabbitmq.servicebus.windows.net:5671/?sasl=plain
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;Address&lt;/code&gt; field we'll enter the name of our &lt;strong&gt;Service Bus Queue&lt;/strong&gt;, in this case, it was called &lt;code&gt;from-rabbitmq&lt;/code&gt;. Click &lt;code&gt;Add Shovel&lt;/code&gt;, and our set up should be ready to start receiving messages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Publishing Messages from RabbitMQ to Azure Service Bus
&lt;/h2&gt;

&lt;p&gt;In the RabbitMQ Management interface we can go to &lt;code&gt;Queues&lt;/code&gt;, select the &lt;code&gt;azure&lt;/code&gt; queue, and search for the &lt;code&gt;Publish message&lt;/code&gt; panel. There a form will appear that will let you publish messages directly to our queue. For our example we are just going to add &lt;code&gt;fist message&lt;/code&gt; as the &lt;code&gt;Payload&lt;/code&gt; and hit &lt;code&gt;Publish Message&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Yvjt1SWj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/k918mdi6rmzvl7pc9z2f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Yvjt1SWj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/k918mdi6rmzvl7pc9z2f.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go back to Azure and inspect your queue. Click &lt;code&gt;Service Bus Explorer&lt;/code&gt; in the left panel. If all went well you'll see your queue now has one message. Yay, congrats!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xaRrbiDf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/flto7xucw2snubkhxlcl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xaRrbiDf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/flto7xucw2snubkhxlcl.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But let's make sure that message is the one you sent from RabbitMQ. Select the &lt;code&gt;Peek&lt;/code&gt; tab and click the &lt;code&gt;Peek&lt;/code&gt; button to retrieve the last messages in your queue. Click on the message to inspect its contents. You should see something like the image below where your &lt;code&gt;first message&lt;/code&gt; is listed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GeJyisYx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/stbk7ttrttcatoduckvv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GeJyisYx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/stbk7ttrttcatoduckvv.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's Recap
&lt;/h2&gt;

&lt;p&gt;Congrats! We did a lot and we managed to get our messages from RabbitMQ to Azure Service Bus, let's recap the steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an Azure Service Bus Namespace&lt;/li&gt;
&lt;li&gt;Add a queue to the namespace&lt;/li&gt;
&lt;li&gt;Add a SAS Policy to our queue&lt;/li&gt;
&lt;li&gt;Get the queue connection string&lt;/li&gt;
&lt;li&gt;Enable the RabbitMQ shovel plugin &amp;amp; the management interface&lt;/li&gt;
&lt;li&gt;Convert the Azure Service Bus connection string to RabbitMQ's AMQP format&lt;/li&gt;
&lt;li&gt;Add a new shovel to RabbitMQ&lt;/li&gt;
&lt;li&gt;Publish messages&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By following the previous steps you were able to integrate areas of your org that might be outside of Azure, by shipping messages from RabbitMQ to Azure Service Bus using the Shovel Plugin. This has enormous advantages since we can even allow trusted third parties to connect their apps with our Azure deployment.&lt;/p&gt;

&lt;p&gt;In the end, messaging is about enabling connections, and with this technique we just opened a new one.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>servicebus</category>
      <category>rabbitmq</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Como agregar un corazón verde 💚 al prompt de tu Terminal</title>
      <dc:creator>Alvaro Videla</dc:creator>
      <pubDate>Thu, 18 Jun 2020 13:07:30 +0000</pubDate>
      <link>https://dev.to/videlalvaro/como-agregar-un-corazon-verde-al-prompt-de-tu-terminal-39ce</link>
      <guid>https://dev.to/videlalvaro/como-agregar-un-corazon-verde-al-prompt-de-tu-terminal-39ce</guid>
      <description>&lt;p&gt;En este pequeño artículo les quiero mostrar como customizar el prompt de su terminal para que muestre un corazón verde 💚.&lt;/p&gt;

&lt;p&gt;Primero dependiendo del sistema en que trabajemos (Mac, Ubuntu, etc.), vamos a tener que editar ya sea el archivo &lt;code&gt;.bashrc&lt;/code&gt;, o &lt;code&gt;.bashprofile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Allí vamos a editar la variable &lt;code&gt;PS1&lt;/code&gt; que es la que controla como luce el prompt. Como puede haber varias de ellas, vamos a identificar cuál es la que afecta nuestro setup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Bo2awE0b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/i88vp9g99xj1um5ekt7g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Bo2awE0b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/i88vp9g99xj1um5ekt7g.png" alt=".bashrc PS1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para ello en la terminal, podemos ejecutar el siguiente comando &lt;code&gt;echo $PS1&lt;/code&gt; que nos va a mostrar el contenido de la variable:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PL8XfvCQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qdky51ug1uuydzhw72rt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PL8XfvCQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qdky51ug1uuydzhw72rt.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Luego vamos al archivo de configuración y allí modificamos nuestra vairable &lt;code&gt;PS1&lt;/code&gt;, agregando el corazón verde 💚 donde más nos guste:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pKhOuJG0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/l1j7vkif8diaod5il0gp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pKhOuJG0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/l1j7vkif8diaod5il0gp.png" alt=".bashrc corazón verde PS1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Y luego si abrimos una nueva terminal va a lucir de este modo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hNPXuCIL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/712iwloyor79mf8qdu4x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hNPXuCIL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/712iwloyor79mf8qdu4x.png" alt="Será Ley"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Notional Machines and The Karl Hassan Question</title>
      <dc:creator>Alvaro Videla</dc:creator>
      <pubDate>Mon, 04 May 2020 18:04:53 +0000</pubDate>
      <link>https://dev.to/videlalvaro/notional-machines-and-the-karl-hassan-question-3jdp</link>
      <guid>https://dev.to/videlalvaro/notional-machines-and-the-karl-hassan-question-3jdp</guid>
      <description>&lt;p&gt;In the article &lt;a href="https://dl.acm.org/doi/10.1145/3265757.3265765"&gt;&lt;em&gt;Thinking out of the box: comparing metaphors for variables in programming education&lt;/em&gt;&lt;/a&gt;, by Hermans et al., they present a study that compares the effectiveness of metaphors when teaching novices about programming variables. For their study, they used two metaphors: variables as a box, and variables as labels that one can place on one value. So the &lt;em&gt;box&lt;/em&gt; metaphors leads to &lt;code&gt;x contains 5&lt;/code&gt;, while the label metaphors leads to &lt;code&gt;x is 5&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once the subjects had their basic programming lessons, they were asked questions to test their understanding. Using Scratch as programming language, the researchers asked questions about the results of a program, and also asked people for explanation of their reasoning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;set points to 0
set points to 2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Question: What is stored in points now?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In that case, some people arrived at the right answer, but their reasoning was wrong. While &lt;code&gt;2&lt;/code&gt; is the right answer, justifying it by saying that 0 + 2 = 2 is not the right reasoning. This problem with mutation is discussed in Joe Armstrong's book Programming Erlang. There he explains how by having a single assignment of variables, Erlang is more familiar to the math one learns in school. &lt;/p&gt;

&lt;p&gt;A more interesting example happens when they asked people to reason about strings and variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;set name to Karl
set name to Hassan
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Question: What is stored in name?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some people answered &lt;code&gt;KarlHassan&lt;/code&gt;, while others replied &lt;code&gt;KarlSan&lt;/code&gt;. I want to focus on the latter, since the authors consider this reply to be a misconception.&lt;/p&gt;

&lt;p&gt;Earlier in the paper the authors mention the idea of a "Notional Machine", which is explained as: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;the general properties that a student assumes of the machine executing their code. It involves various aspects related to the program: compiler, memory management, etc. Having an incorrect understanding of the notional machine of a programming language is believed to be the cause of many misconceptions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I think this idea of &lt;em&gt;notional machine&lt;/em&gt; is critical when it comes to understanding if &lt;em&gt;KarlSan&lt;/em&gt; qualifies as a misconception. To speak of a misconception, means that there is a correct answer according to some &lt;em&gt;notional machine&lt;/em&gt;. Subjects received basic programming lessons, which are not included in the paper, so it's not clear in the paper if the researchers shared their own (correct?) notional machine with their subjects.&lt;/p&gt;

&lt;p&gt;Code executes in a particular context, and there are contexts for which KarlSan is a correct answer. &lt;/p&gt;

&lt;p&gt;Let's talk about Java, and its &lt;a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.7"&gt;Non-Atomic Treatment of &lt;code&gt;double&lt;/code&gt; and &lt;code&gt;long&lt;/code&gt;&lt;/a&gt;. As Goetz explains in Java Concurrency in Practice, the JVM could "treat a 64-bit read and write as two separate 32-bit operations." In a multithreaded environment, it's possible to read 32 bits of one value, and 32 bits of another! Transposing this to our example, there's a notional machine for which KarlSan is the right answer. We could easily create such a scenario with an hypothetical 8 bit computer, where unless the programming language updates strings of bytes atomically, there would be no guarantee that &lt;em&gt;KarlSan&lt;/em&gt; wouldn't happen.&lt;/p&gt;

&lt;p&gt;Without a shared notional machine, it's hard to evaluate the subject's answers--KarlSan--as invalid or "misconceptions".&lt;/p&gt;

&lt;h2&gt;
  
  
  Overwriting data metaphor
&lt;/h2&gt;

&lt;p&gt;In section 5.3 of the papers the authors write:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;we observed that participants often tried to calculate with the given values in Q7, i.e. adding 0 and 2 together, rather than understanding that 2 &lt;em&gt;overwrites&lt;/em&gt; the existing value 0. [emphasis mine]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The word &lt;em&gt;overwrite&lt;/em&gt; is very interesting because it's a metaphor that semantically departs from the notion of &lt;em&gt;variables as labels&lt;/em&gt; or &lt;em&gt;variables as boxes&lt;/em&gt;. Would it be better to explain variable assignment as "overwriting" sectors of memory?&lt;/p&gt;

&lt;h2&gt;
  
  
  Metaphors and Notional Machines
&lt;/h2&gt;

&lt;p&gt;A quick note on &lt;em&gt;notional machines&lt;/em&gt;. In &lt;a href="https://www.felienne.com/archives/6375"&gt;So… what is a notional machine?&lt;/a&gt; Hermans talks of these devices as being scaffolding that help us understand other ideas. How close is this to Lakoff's &amp;amp; Núñez idea of metaphorical scaffolding, as presented in their book &lt;a href="https://en.wikipedia.org/wiki/Where_Mathematics_Comes_From"&gt;Where Mathematics Comes From&lt;/a&gt;? &lt;/p&gt;

&lt;p&gt;When explaining concepts like Sets, the line number (another metaphor), and so on, they say that we understand Sets (or any other mathematical concept), because we have built over the years a scaffolding of metaphors that finally let us support the idea of a Set. It'd be interesting to perform a similar exercise for programming, selecting one programming language and going over the many metaphors that help us understand what a function does, or what a loop does, and so on.&lt;/p&gt;

&lt;h2&gt;
  
  
  while(feedback) learn
&lt;/h2&gt;

&lt;p&gt;Another question for the study is to ask if a one shot exam is a good measurement for understanding which metaphor is better, the box one or the label one. Because learning is not a one shot opportunity. We learn through feedback, by applying what we have learnt, even if through a misconception, and then later receiving correcting nudges that put us in the right direction. In the long run, which metaphor is actually &lt;em&gt;better&lt;/em&gt;? &lt;/p&gt;

&lt;p&gt;Another question is &lt;em&gt;embodiment&lt;/em&gt;. Certain metaphors make sense due to our past experiences, our own body, our abilities, our culture, mother language, and so on. While it's clear that the study was targeted at either Dutch or English speakers, it’d be interesting to find out which metaphors work better in other cultures, like in Chinese or Ewe. In Fundamentals of Comparative And Intercultural Philosophy, Ma and Van Brakel explain how the concept of "to be" is divided among several verbs in the African language Ewe. How would this affect the idea of variable assignment, and which metaphors would be more apt in such cases?&lt;/p&gt;

&lt;p&gt;Due to embodiment, objects present us with affordances--clues that tell us how an object can be used. In &lt;em&gt;Vision in Design&lt;/em&gt;, Hekkert and van Dijk explain that a ceiling is a place to hang things from, like lightbulbs, but not a place to walk. Not for a fly though, for them a ceiling is a walking surface. “What is the purpose of a pen for someone who has lost his hands? Is it still a ‘tool for writing’?”. &lt;/p&gt;

&lt;p&gt;In the same vein we can ask which life experiences, abilities, cultural background, and so on, make a box or a label more &lt;em&gt;fit&lt;/em&gt; as metaphors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The paper by Hermans definitely opens some interesting questions, from metaphors to the &lt;em&gt;rightness&lt;/em&gt; of notional machines. Sharing notional machines seems to be a precondition for evaluating the effectiveness of metaphors, perhaps to the point where finding Lakoff's scaffolding for programming could be a crucial development to reason about how to best teach and learn programming. When discussing the &lt;em&gt;rightness&lt;/em&gt; of metaphors, it would be interesting to be one the lookout for metaphors that might pop up when explaining concepts, like the &lt;em&gt;overwrite&lt;/em&gt; metaphor presented above. Perhaps that was the &lt;em&gt;right&lt;/em&gt; metaphor all along?&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The Code Speaks On Its Own, NOT</title>
      <dc:creator>Alvaro Videla</dc:creator>
      <pubDate>Thu, 23 Apr 2020 17:33:00 +0000</pubDate>
      <link>https://dev.to/videlalvaro/the-code-speaks-on-its-own-not-3jf9</link>
      <guid>https://dev.to/videlalvaro/the-code-speaks-on-its-own-not-3jf9</guid>
      <description>&lt;p&gt;If we receive a letter saying: "you must stay home, you can only go out for grocery shopping", we won't just follow it, first we'll check who sent it. If it comes from an authority, then we'll follow its orders.&lt;/p&gt;

&lt;p&gt;To interpret a text we need to see its context, and context is provided not by the text itself, but by things that surround it: book titles, publisher's info, jacket copy, author, date published (april's fool anyone?), and so on. In Literary Theory, these devices are called &lt;em&gt;paratexts&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Paratexts are key elements when it comes to interpreting a text, to understanding as a reader how we are supposed to use and interpret the text. They guide us as we find meaning. We need paratexts, because text doesn't speak on its own. To interpret it we need context.&lt;/p&gt;

&lt;p&gt;Code works the same. We have comments, type declarations, package names, compiler flags, and many more things that provide clues on how to interpret the code, because code on its own doesn't speak for itself.&lt;/p&gt;

&lt;p&gt;Let's see some examples from &lt;a href="https://code.visualstudio.com/?WT.mc_id=devto-blog-alvidela" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt; to understand how context is important when reading code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two classes that were almost the same
&lt;/h2&gt;

&lt;p&gt;Let's take a look at the class OpenIssueReporterArgs, which appears twice in VSC. The first time inside the file &lt;code&gt;vscode/src/vs/workbench/contrib/issue/common/commands.ts&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;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;OpenIssueReporterArgs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;extensionId&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;readonly&lt;/span&gt; &lt;span class="nx"&gt;issueTitle&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;readonly&lt;/span&gt; &lt;span class="nx"&gt;issueBody&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the second one inside the file &lt;code&gt;vscode/src/vs/workbench/api/common/apiCommands.ts&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;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;OpenIssueReporterArgs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;extensionId&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;readonly&lt;/span&gt; &lt;span class="nx"&gt;issueTitle&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;readonly&lt;/span&gt; &lt;span class="nx"&gt;issueBody&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These interfaces are used by the Issue Reporter Tool (see image), they specify the interface for the arguments passed to the tool. The interfaces are almost identical, the only difference being that in the second interface &lt;code&gt;extensionId&lt;/code&gt; is required. So how do we know what they do? They don't seem to be speaking too much on their own, do they?&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%2Fwp8byca95052mogdkg2t.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%2Fwp8byca95052mogdkg2t.png" alt="Issue Reporter Tool"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Remember we said package names act as paratext, they can help us interpret code by providing context? In this case our first interface lives inside &lt;code&gt;vscode/src/vs/workbench/contrib&lt;/code&gt;, while the second one is inside &lt;code&gt;vscode/src/vs/workbench/api&lt;/code&gt;. Inside &lt;code&gt;contrib&lt;/code&gt; we have &lt;em&gt;contributions&lt;/em&gt; that add features to VSC like full text search or git. One of those contributions is the Issue Reporter Tool. These contributions can be used from VSC, while the second one is inside &lt;code&gt;api&lt;/code&gt;, which can be used by extensions. Extensions must provide their ID when reporting issues, while VSC doesn't need to do that. That's why for the &lt;code&gt;apiCommands&lt;/code&gt; version of &lt;code&gt;OpenIssueReporterArgs&lt;/code&gt; the parameter &lt;code&gt;extensionId&lt;/code&gt; is required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Comments are Hard
&lt;/h2&gt;

&lt;p&gt;Another place where we see paratexts is in code comments. An evergreen problem with comments is that they tend to lose touch with code, falling behind from actual features, or even commenting about stuff that's not there anymore. But don't worry, we aren't the only ones with this problem. Not even Cervantes escaped this fate: in Don Quixote, the description for Chapter X doesn’t match the contents of the chapter. When they reformatted the book, the chapter description stopped matching its contents.&lt;/p&gt;

&lt;p&gt;In the case of Visual Studio Code, it has some great docs for its APIs, detailing what a function is doing, the expected types and so on. But even there, we might find things that are confusing. Take a look at this code from &lt;code&gt;vscode/src/vs/base/common/collections.ts&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="cm"&gt;/**
 * Returns an array which contains all values that reside
 * in the given set.`
 */&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;values&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IStringDictionary&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;INumberDictionary&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&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;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&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;let&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;)&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;hasOwnProperty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="nx"&gt;key&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;result&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 docs talk about a &lt;code&gt;set&lt;/code&gt;, while the function accepts a &lt;code&gt;dictionary&lt;/code&gt;. In my article &lt;a href="https://queue.acm.org/detail.cfm?id=3127495" rel="noopener noreferrer"&gt;Metaphors We Compute By&lt;/a&gt; I discuss how names are important because they augment the understanding we have of situations, or in this case, of pieces of code. A &lt;code&gt;set&lt;/code&gt; implies uniqueness of elements. Moreover, a &lt;code&gt;set&lt;/code&gt; offers certain operations like intersection, union, and so on, that is not clear at first sight how they would make sense for a &lt;code&gt;dictionary&lt;/code&gt;, if at all.&lt;/p&gt;

&lt;p&gt;So I opened a &lt;a href="https://github.com/microsoft/vscode/pull/92859" rel="noopener noreferrer"&gt;PR&lt;/a&gt;, updating the comments for those functions. While I was doing the final commit, I realized that there were more functions inside that API that mentioned &lt;code&gt;set&lt;/code&gt; instead of &lt;code&gt;dictionary&lt;/code&gt;. Again, keeping docs &amp;amp; comments in sync is hard. I think having just one function fixed saying &lt;code&gt;dictionary&lt;/code&gt; while the others said &lt;code&gt;set&lt;/code&gt; would have been even worse, since a future reader might be led to think they do different things.&lt;/p&gt;

&lt;p&gt;About this issue, a &lt;a href="https://twitter.com/bitandbang" rel="noopener noreferrer"&gt;colleague&lt;/a&gt; introduced me to a couple of tools that could help with this. One of them is &lt;a href="https://github.com/electron/docs-parser" rel="noopener noreferrer"&gt;electron/docs-parser&lt;/a&gt;. Then we have &lt;a href="https://github.com/electron/typescript-definitions" rel="noopener noreferrer"&gt;electron/typescript-definitions&lt;/a&gt;, and finally &lt;a href="https://github.com/electron/archaeologist" rel="noopener noreferrer"&gt;electron/archaeologist&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;With &lt;code&gt;docs-parser&lt;/code&gt; they generate an AST out of the documentation found in their TypeScript code. Then that AST is used by &lt;code&gt;typescript-definitions&lt;/code&gt; to output actual TypeScript types. Finally with &lt;code&gt;archaeologist&lt;/code&gt; they make sure that whenever a pull request is submitted against Electron, the types as they appear in the functions, match the types described in the docs. For all this to work, Electron developers have to follow a &lt;a href="https://github.com/electron/electron/blob/master/docs/styleguide.md" rel="noopener noreferrer"&gt;documentation style guide&lt;/a&gt;. I think this is a step in the right direction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article we showed how "code speaks on its own" is just a myth. All texts, including code, work because they are assisted by many devices that help us interpret them. In Literary Theory, some of those devices are called &lt;em&gt;paratexts&lt;/em&gt;. We saw paratexts appear in code in the shape of comments, types, package names and so on. &lt;/p&gt;

&lt;p&gt;We saw how to understand an interface that was almost identical with another one, we had to resort to package names to understand its purpose. &lt;/p&gt;

&lt;p&gt;Finally about comments we learnt that as with Don Quixote, we need to make sure our paratexts match the content they are trying to describe, otherwise they just add confusion. In the case of comments matching code, we saw that the people behind Electron are creating tools that move us forward in the right direction.&lt;/p&gt;

</description>
      <category>vsc</category>
      <category>literarytheory</category>
      <category>programming</category>
      <category>visualstudiocode</category>
    </item>
    <item>
      <title>Inside Visual Studio Code - Classes and Interfaces</title>
      <dc:creator>Alvaro Videla</dc:creator>
      <pubDate>Thu, 02 Apr 2020 15:38:27 +0000</pubDate>
      <link>https://dev.to/videlalvaro/inside-visual-studio-code-classes-and-interfaces-59hn</link>
      <guid>https://dev.to/videlalvaro/inside-visual-studio-code-classes-and-interfaces-59hn</guid>
      <description>&lt;p&gt;I've been trying to dive into the internals of &lt;a href="https://code.visualstudio.com/?WT.mc_id=devto-blog-alvidela"&gt;Visual Studio Code&lt;/a&gt;, so I wanted to have a tool that would let me see the names of classes and interfaces declared in VSC own &lt;a href="https://github.com/microsoft/vscode"&gt;code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This tool would traverse VSC own code, load the TypeScript files defined there, and then for each file it would output something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;ColorPickerBody|/path/to/vscode/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts
ColorPickerHeader|/path/to/vscode/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;ColorPickerBody&lt;/code&gt; and &lt;code&gt;ColorPickerHeader&lt;/code&gt; are two classes defined inside &lt;code&gt;colorPickerWidget.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Yesterday I &lt;a href="https://twitter.com/old_sound/status/1245440323590991872?s=20"&gt;asked&lt;/a&gt; on Twitter if someone knew about a tool that could extract this information from a TypeScript file. &lt;a href="https://twitter.com/gillchristian/status/1245444857377837056?s=20"&gt;Someone&lt;/a&gt; recommended that I use &lt;a href="https://github.com/dsherret/ts-morph"&gt;ts-morph&lt;/a&gt;, which after taking a look at the examples on their docs, seemed to do just what I wanted.&lt;/p&gt;

&lt;p&gt;So I started a new TypeScript project, npm'ed &lt;code&gt;ts-morph&lt;/code&gt; and then built something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ClassDeclaration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;InterfaceDeclaration&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ts-morph&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;project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addSourceFilesAtPaths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../vscode/src/vs/**/*.ts&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;sourceFiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getSourceFiles&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;let&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;sourceFiles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getFilePath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;classDeclarations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getClasses&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;interfaceDeclarations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getInterfaces&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;let&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;classDeclarations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;interfaceDeclarations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let's see that in detail. First we import the required types from &lt;code&gt;ts-morph&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ClassDeclaration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;InterfaceDeclaration&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ts-morph&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then we create a new project, and we add source files to it. In this case we asume the location of the VSC repo–of course you could provide this as a CLI argument.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addSourceFilesAtPaths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../vscode/src/vs/**/*.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We are going to traverse those files to extract class and interface definitions. We do that by calling &lt;code&gt;project.getSourceFiles()&lt;/code&gt;, which returns an array of &lt;code&gt;SourceFile&lt;/code&gt;. &lt;a href="https://github.com/dsherret/ts-morph/blob/85a173fd7102585fcc1ad2c8335716160ee0d4ea/docs/details/source-files.md"&gt;SourceFile&lt;/a&gt; is a class that can return a lot of information about a TypeScript file, from it's file name, to the imports declared there, and so on. &lt;/p&gt;

&lt;p&gt;Once we have the &lt;code&gt;SourceFiles[]&lt;/code&gt; we are going to iterate it, to output the information we need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;sourceFiles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getFilePath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;classDeclarations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getClasses&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;interfaceDeclarations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getInterfaces&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;let&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;classDeclarations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;interfaceDeclarations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As you can see each &lt;code&gt;SourceFile&lt;/code&gt; can tell us about its name, via &lt;code&gt;f.getFilePath()&lt;/code&gt;, and also return the declared classes and interfaces via &lt;code&gt;f.getClasses()&lt;/code&gt; and &lt;code&gt;f.getInterfaces()&lt;/code&gt; respectively.&lt;/p&gt;

&lt;p&gt;We use two for loops, where we output the information in the following format: &lt;code&gt;className|fileName&lt;/code&gt;, or &lt;code&gt;interfaceName|fileName&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And that's it, after we compile the project, we can run it from the CLI and get the information we want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;IStaticExtension&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="sr"&gt;/path/&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;vs&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;workbench&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;workbench&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="nx"&gt;ICommontTelemetryPropertiesResolver&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="sr"&gt;/path/&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;vs&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;workbench&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;workbench&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="nx"&gt;IExternalUriResolver&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="sr"&gt;/path/&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;vs&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;workbench&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;workbench&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="nx"&gt;TunnelOptions&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="sr"&gt;/path/&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;vs&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;workbench&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;workbench&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You might have been wondering why I want the information output in that format. I want to be able to pass this file to bash's &lt;code&gt;sort&lt;/code&gt; and have an easy way to see which class &amp;amp; interface definition name are repeated across the project. &lt;/p&gt;

&lt;p&gt;Why? Soon I'll write another article explaining why.&lt;/p&gt;

</description>
      <category>vsc</category>
      <category>visualstudiocode</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
