{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# The `ClockExtension` Sample\n", "\n", "The `ClockExtension` sample walks you through how to create a simple .NET Interactive extension and then package it using NuGet.\n", "\n", "## 1. Build the project\n", "\n", "If you opened this notebook so that its working directory is in the `ClockExtension` project directory, then the following cell will work. Otherwise, you should first switch your working directory to the directory containing this notebook." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "dotnet_interactive": { "language": "pwsh" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "MSBuild version 17.8.3+195e7f5a3 for .NET\n", " Determining projects to restore...\n", " All projects are up-to-date for restore.\n", " ClockExtension -> C:\\dev\\interactive\\samples\\extensions\\ClockExtension\\bin\\Debug\\net8.0\\ClockExtension.dll\n", "\n", "Build succeeded.\n", " 0 Warning(s)\n", " 0 Error(s)\n", "\n", "Time Elapsed 00:00:02.68\n", "MSBuild version 17.8.3+195e7f5a3 for .NET\n", " Determining projects to restore...\n", " All projects are up-to-date for restore.\n", " ClockExtension -> C:\\dev\\interactive\\samples\\extensions\\ClockExtension\\bin\\Release\\net8.0\\ClockExtension.dll\n", "C:\\Program Files\\dotnet\\sdk\\8.0.100\\Sdks\\NuGet.Build.Tasks.Pack\\build\\NuGet.Build.Tasks.Pack.targets(221,5): warning NU5104: A stable release of a package should not have a prerelease dependency. Either modify the version spec of dependency \"microsoft.dotnet.interactive [1.0.0-beta.23562.1, )\" or update the version field in the nuspec. [C:\\dev\\interactive\\samples\\extensions\\ClockExtension\\ClockExtension.csproj]\n", "C:\\Program Files\\dotnet\\sdk\\8.0.100\\Sdks\\NuGet.Build.Tasks.Pack\\build\\NuGet.Build.Tasks.Pack.targets(221,5): warning NU5104: A stable release of a package should not have a prerelease dependency. Either modify the version spec of dependency \"microsoft.dotnet.interactive.csharp [1.0.0-beta.23562.1, )\" or update the version field in the nuspec. [C:\\dev\\interactive\\samples\\extensions\\ClockExtension\\ClockExtension.csproj]\n", " The package ClockExtension.1.0.0 is missing a readme. Go to https://aka.ms/nuget/authoring-best-practices/readme to learn why package readmes are important.\n", " Successfully created package 'C:\\dev\\interactive\\samples\\extensions\\ClockExtension\\bin\\Release\\ClockExtension.1.0.0.nupkg'.\n", "\n", " Directory: C:\\dev\\interactive\\samples\\extensions\\ClockExtension\\bin\\Debug\n", "\n", "\u001b[32;1mMode \u001b[0m\u001b[32;1m LastWriteTime\u001b[0m\u001b[32;1m Length\u001b[0m\u001b[32;1m Name\u001b[0m\n", "\u001b[32;1m---- \u001b[0m \u001b[32;1m -------------\u001b[0m \u001b[32;1m ------\u001b[0m \u001b[32;1m----\u001b[0m\n", "-a--- 12/5/2023 9:34 AM 9220 \u001b[31;1mClockExtension.1.0.0.nupkg\u001b[0m\n", "\n", " Directory: C:\\dev\\interactive\\samples\\extensions\\ClockExtension\\bin\\Release\n", "\n", "\u001b[32;1mMode \u001b[0m\u001b[32;1m LastWriteTime\u001b[0m\u001b[32;1m Length\u001b[0m\u001b[32;1m Name\u001b[0m\n", "\u001b[32;1m---- \u001b[0m \u001b[32;1m -------------\u001b[0m \u001b[32;1m ------\u001b[0m \u001b[32;1m----\u001b[0m\n", "-a--- 12/5/2023 12:37 PM 9192 \u001b[31;1mClockExtension.1.0.0.nupkg\u001b[0m\n", "\n" ] } ], "source": [ "# 1. Build the project\n", "dotnet build ClockExtension\n", "\n", "# Clear any older versions of this extension package from your NuGet cache\n", "rm ~/.nuget/packages/ClockExtension -Force -Recurse -ErrorAction Ignore\n", "\n", "# Pack up the NuGet package. \n", "dotnet pack ClockExtension /p:PackageVersion=1.0.0\n", "\n", "# 3. Check that the package is there\n", "Get-ChildItem -Recurse ClockExtension*.nupkg\n" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Load the NuGet package \n", "\n", "Now we're ready to install the extension that packaged up in the NuGet package we just built. We can use the [`#i` directive](https://github.com/dotnet/interactive/blob/main/docs/nuget-overview.md) to add the build output folder to our NuGet sources.\n", "\n", "First, let's make sure the file is there like we expect after the build.\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [ { "data": { "text/plain": [ "✅ The package is there!" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "using System.IO;\n", "\n", "// Create an absolute path since #i doesn't like \n", "var debugOutputFolder = new DirectoryInfo(@\".\\ClockExtension\\bin\\Release\\\").FullName;\n", "\n", "if (File.Exists(Path.Combine(debugOutputFolder, \"ClockExtension.1.0.0.nupkg\")))\n", "{\n", " \"✅ The package is there!\".Display();\n", "} \n", "else\n", "{\n", " \"❌ Something must have gone wrong with the build. The package isn't there.\".Display();\n", "}\n", "\n", "var nugetSource = $\"nuget:{debugOutputFolder}\";" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "If the package is there, we can include its location as a NuGet source using the `#i` directive. The following syntax shares the `#i` argument including the `nuget:` prefix and the computed fully-qualified path." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [ { "data": { "text/html": [ "
Restore sources
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "#i @csharp:nugetSource" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Now that the package source is added, we can use `#r` to install the package. " ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "dotnet_interactive": { "language": "csharp" } }, "outputs": [ { "data": { "text/html": [ "
Restore sources
Installed Packages
" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "Loading extension script from `C:\\Users\\josequ\\.nuget\\packages\\clockextension\\1.0.0\\interactive-extensions\\dotnet\\extension.dib`" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
ClockExtension is loaded. It adds visualizations for System.DateTime and System.DateTimeOffset. Try it by running: DateTime.Now or #!clock -h
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "#r \"nuget:ClockExtension,1.0.0\"" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Try the extension\n", "\n", "As you can see from the output above, the extension is able to explain a bit about what it does. So now we can try it out.\n", "\n", "It adds a custom formatter for `System.DateTime`:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "dotnet_interactive": { "language": "csharp" } }, "outputs": [ { "data": { "text/html": [ "
.NET Interactive
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "DateTime.Now" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The extension output also advised us to run `#!clock -h`. Extensions can add magic commands and all magic commands can provide help." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "dotnet_interactive": { "language": "csharp" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Description:\r\n", " Displays a clock showing the current or specified time.\r\n", "\r\n", "Usage:\r\n", " #!clock [options]\r\n", "\r\n", "Options:\r\n", " -o, --hour The position of the hour hand\r\n", " -m, --minute The position of the minute hand\r\n", " -s, --second The position of the second hand\r\n", " -?, -h, --help Show help and usage information\r\n", "\r\n", "\r\n" ] } ], "source": [ "#!clock -h" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `#!clock` magic command help explains how to use options to set the position of the hands on the clock:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "dotnet_interactive": { "language": "csharp" } }, "outputs": [ { "data": { "text/html": [ "
.NET Interactive
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "#!clock --hour 12 -m 34 -s 56" ] } ], "metadata": { "kernelspec": { "display_name": ".NET (C#)", "language": "C#", "name": ".net-csharp" }, "language_info": { "name": "polyglot-notebook" }, "polyglot_notebook": { "kernelInfo": { "defaultKernelName": "csharp", "items": [ { "aliases": [], "name": ".NET" }, { "aliases": [ "C#", "c#" ], "languageName": "C#", "name": "csharp" }, { "aliases": [ "F#", "f#" ], "languageName": "F#", "name": "fsharp" }, { "aliases": [], "languageName": "HTML", "name": "html" }, { "aliases": [], "languageName": "KQL", "name": "kql" }, { "aliases": [], "languageName": "Mermaid", "name": "mermaid" }, { "aliases": [ "powershell" ], "languageName": "PowerShell", "name": "pwsh" }, { "aliases": [], "languageName": "SQL", "name": "sql" }, { "aliases": [], "name": "value" }, { "aliases": [ "frontend" ], "name": "vscode" } ] } } }, "nbformat": 4, "nbformat_minor": 2 }