diff --git a/AsyncSingleFileGenerator/AsyncSingleFileGeneratorSample.sln b/AsyncSingleFileGenerator/AsyncSingleFileGeneratorSample.sln
new file mode 100644
index 00000000..492d80bf
--- /dev/null
+++ b/AsyncSingleFileGenerator/AsyncSingleFileGeneratorSample.sln
@@ -0,0 +1,30 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27620.3002
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SingleFileGeneratorSample", "src\SingleFileGeneratorSample.csproj", "{154F8BFA-6D1D-4FEF-95AC-95D4222B34DA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D8854F3A-BEAA-44E0-A140-413225798789}"
+ ProjectSection(SolutionItems) = preProject
+ README.md = README.md
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {154F8BFA-6D1D-4FEF-95AC-95D4222B34DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {154F8BFA-6D1D-4FEF-95AC-95D4222B34DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {154F8BFA-6D1D-4FEF-95AC-95D4222B34DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {154F8BFA-6D1D-4FEF-95AC-95D4222B34DA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {C1C1BBB7-1562-4820-8E03-795009DFE966}
+ EndGlobalSection
+EndGlobal
diff --git a/AsyncSingleFileGenerator/LICENSE b/AsyncSingleFileGenerator/LICENSE
new file mode 100644
index 00000000..49d21669
--- /dev/null
+++ b/AsyncSingleFileGenerator/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Microsoft
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/AsyncSingleFileGenerator/README.md b/AsyncSingleFileGenerator/README.md
new file mode 100644
index 00000000..36ddd3b2
--- /dev/null
+++ b/AsyncSingleFileGenerator/README.md
@@ -0,0 +1,153 @@
+# Single File Generator sample
+
+**Applies to Visual Studio 2017 and newer**
+
+This example shows how to write an Asynchronous Single File Generator in a Visual Studio extension that will output a nested file when the parent file is modified.
+
+> [!NOTE]
+> Please note that there are two flavours of Single File Generators (aka Custom Tools) in Visual Studio:
+> * Single File Generators that run synchronously on the UI thread
+> * Asynchronous Single File Generators that run on a background thread (VS 2017 and newer)
+
+This sample demonstrates how to create an **Asynchronous Single File Generator** that is compatible with synchrouns one and tries to use async APIs where possible otherwise will fallback to sync APIs.
+
+To test the asynchronous behavior you will need to apply the code generator to a CPS-based project like a .NET Core project, or any SDK-style project.
+
+Clone the repo to test out the sample in Visual Studio 2017 yourself.
+
+## What is a Single File Generator
+A Single File Generator is a mechanism that will auto-create and nest an output file when the source file changes. In this sample, the generator is applied to a **.js** file that will then output a **.min.js** file like so:
+
+
+
+It is also known as a Custom Tool which can be manually set in the properties of supported files.
+
+
+
+The most well-known examples of existing generators are the ones creating a strongly typed C#/VB nested file for .resx files.
+
+Every time the file with the Custom Tool property is modified, the Single File Generator will execute to update the nested file.
+
+The nested file can be of any type - code, image, etc. - the sky is the limit.
+
+## Let's get started
+Before we begin, make sure you have created a VSIX project in Visual Studio. See how to [create a VSIX project](https://docs.microsoft.com/en-us/visualstudio/extensibility/extensibility-hello-world) if you don't already have one ready.
+
+### Install NuGet package
+The base classes for the Single File Generator are located in the [Microsoft.VisualStudio.TextTemplating.VSHost.15.0](https://www.nuget.org/packages/Microsoft.VisualStudio.TextTemplating.VSHost.15.0/) NuGet package, so go ahead and install that into your VSIX project.
+
+We also need the [Nuglify](https://www.nuget.org/packages/NUglify/) NuGet package that can minify JavaScript.
+
+### The generator
+The generator is a simple class that inherits from the *BaseCodeGeneratorWithSite* and has 2 methods for us to implement.
+
+```c#
+using Microsoft.VisualStudio.TextTemplating.VSHost;
+
+[Guid("82ca81c8-b507-4ba1-a33d-ff6cdad20e36")] // change this GUID
+public sealed class MinifyCodeGenerator : BaseCodeGeneratorWithSite
+{
+ public override string GetDefaultExtension()
+ {
+ return ".min.js";
+ }
+
+ protected override byte[] GenerateCode(string inputFileName, string inputFileContent)
+ {
+ UglifyResult minified = Uglify.Js(inputFileContent);
+ return Encoding.UTF8.GetBytes(minified.Code);
+ }
+}
+```
+
+[See full generator class in the source](src/Generators/MinifyGenerator.cs).
+
+That's it, you now have a Single File Generator that writes a .min.js file with the minified content of the source .js file. Now we must register the generator to make it work.
+
+### Registering the generator
+Decorate your *Package* class with the `ProvideCodeGenerator` attribute.
+
+```c#
+[ProvideCodeGenerator(typeof(MinifyCodeGenerator), nameof(MinifyCodeGenerator), "Minifies JavaScript", true)]
+public class VSPackage : AsyncPackage
+{
+ ...
+}
+```
+
+[See full Package class in the source](src/VSPackage.cs).
+
+> Note: if you don't have a *Package* class, add one to your project using the Add New Item dialog. The template is called *Visual Studio AsyncPackage* in VS 2017.7
+
+Now the generator is registered, and you can now manually give the Custom Tool property on .js files the *MinifyCodeGenerator* value.
+
+That's it. We've now implemented a Single File Generator that minifies JavaScript files.
+
+However, it would be much easier if we give our users a command in the context-menu of files in Solution Explorer to add the value for them so they don't have to type *MinifyCodeGenerator* in the Property Grid manually.
+
+### Add the command button
+In the .VSCT file you must specify a new button. It could look like this:
+
+```c#
+
+```
+
+[See full .vsct file in the source](src/VSCommandTable.vsct).
+
+That will place the button in the context-menu in Solution Explorer.
+
+
+
+Then we need to add the command handler C# file. It will look similar to this:
+
+```c#
+internal sealed class ApplyCustomTool
+{
+ private const int _commandId = 0x0100;
+ private static readonly Guid _commandSet = new Guid("4aaf93c0-70ae-4a4b-9fb6-1ad3997a9adf");
+ private static DTE _dte;
+
+ public static async Task InitializeAsync(AsyncPackage package)
+ {
+ _dte = await package.GetServiceAsync(typeof(DTE)) as DTE;
+
+ var commandService = await package.GetServiceAsync((typeof(IMenuCommandService))) as IMenuCommandService;
+ var cmdId = new CommandID(_commandSet, _commandId);
+ var cmd = new MenuCommand(OnExecute, cmdId)
+ commandService.AddCommand(cmd);
+ }
+
+ private static void OnExecute(object sender, EventArgs e)
+ {
+ ProjectItem item = _dte.SelectedItems.Item(1).ProjectItem;
+ item.Properties.Item("CustomTool").Value = nameof(MinifyCodeGenerator);
+ }
+}
+```
+
+[See full command handler in the source](src/Commands/ApplyCustomTool.cs).
+
+And then finally initialize the command handler from the *Package* initialization method.
+
+```c#
+protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress)
+{
+ await ApplyCustomTool.InitializeAsync(this);
+}
+```
+
+[See full Package class in the source](src/VSPackage.cs).
+
+### Single File Generators in the wild
+Here are more samples of open source extensions implementing Single File Generators.
+
+* [VSIX Synchronizer](https://github.com/madskristensen/VsixSynchronizer)
+* [Extensibility Tools](https://github.com/madskristensen/extensibilitytools)
+
+## License
+[Apache 2.0](LICENSE)
\ No newline at end of file
diff --git a/AsyncSingleFileGenerator/art/code-behind.png b/AsyncSingleFileGenerator/art/code-behind.png
new file mode 100644
index 00000000..2e52dd0b
Binary files /dev/null and b/AsyncSingleFileGenerator/art/code-behind.png differ
diff --git a/AsyncSingleFileGenerator/art/context-menu.png b/AsyncSingleFileGenerator/art/context-menu.png
new file mode 100644
index 00000000..f977f1b0
Binary files /dev/null and b/AsyncSingleFileGenerator/art/context-menu.png differ
diff --git a/AsyncSingleFileGenerator/art/property-grid.png b/AsyncSingleFileGenerator/art/property-grid.png
new file mode 100644
index 00000000..898e1950
Binary files /dev/null and b/AsyncSingleFileGenerator/art/property-grid.png differ
diff --git a/AsyncSingleFileGenerator/src/Commands/ApplyCustomTool.cs b/AsyncSingleFileGenerator/src/Commands/ApplyCustomTool.cs
new file mode 100644
index 00000000..3bfc450d
--- /dev/null
+++ b/AsyncSingleFileGenerator/src/Commands/ApplyCustomTool.cs
@@ -0,0 +1,44 @@
+namespace AsyncSingleFileGeneratorSample;
+
+using System;
+using System.ComponentModel.Design;
+using System.IO;
+using Microsoft.VisualStudio.Shell;
+using Task = System.Threading.Tasks.Task;
+using Microsoft.VisualStudio.Interop;
+using EnvDTE;
+
+internal sealed class ApplyCustomTool
+{
+ private const int _commandId = 0x0100;
+ private static readonly Guid _commandSet = new Guid("4aaf93c0-70ae-4a4b-9fb6-1ad3997a9adf");
+ private static DTE _dte;
+
+ public static async Task InitializeAsync(AsyncPackage package)
+ {
+ ThreadHelper.ThrowIfNotOnUIThread();
+
+ _dte = await package.GetServiceAsync(typeof(DTE)) as DTE;
+
+ var commandService = await package.GetServiceAsync((typeof(IMenuCommandService))) as IMenuCommandService;
+ var cmdId = new CommandID(_commandSet, _commandId);
+
+ var cmd = new OleMenuCommand(OnExecute, cmdId)
+ {
+ // This will defer visibility control to the VisibilityConstraints section in the .vsct file
+ Supported = false
+ };
+
+ commandService.AddCommand(cmd);
+ }
+
+ private static void OnExecute(object sender, EventArgs e)
+ {
+ ProjectItem item = _dte.SelectedItems.Item(1).ProjectItem;
+
+ if (item != null)
+ {
+ item.Properties.Item("CustomTool").Value = MinifyCodeGenerator.Name;
+ }
+ }
+}
diff --git a/AsyncSingleFileGenerator/src/Generators/BaseCodeGeneratorWithSiteAsync.cs b/AsyncSingleFileGenerator/src/Generators/BaseCodeGeneratorWithSiteAsync.cs
new file mode 100644
index 00000000..d94f2a0c
--- /dev/null
+++ b/AsyncSingleFileGenerator/src/Generators/BaseCodeGeneratorWithSiteAsync.cs
@@ -0,0 +1,209 @@
+namespace AsyncSingleFileGeneratorSample;
+
+using Microsoft.VisualStudio;
+using Microsoft.VisualStudio.OLE.Interop;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Shell.Generators;
+using Microsoft.VisualStudio.Shell.Interop;
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using ServiceProvider = Microsoft.VisualStudio.Shell.ServiceProvider;
+
+public abstract class BaseCodeGeneratorWithSiteAsync : IObjectWithSite, IDisposable, IVsSingleFileGenerator, IVsSingleFileGeneratorAsync
+{
+ private object site;
+ private ServiceProvider serviceProvider;
+ private ServiceProvider globalProvider;
+ private bool _disposed;
+
+ ///
+ /// Get a wrapper on the containing project system's Service provider
+ ///
+ ///
+ /// This is a limited service provider that can only reliably provide VxDTE::SID_SVSProjectItem
+ /// SID_SVSWebReferenceDynamicProperties IID_IVsHierarchy SID_SVsApplicationSettings
+ /// To get the global provider, call GetSite on IVSHierarchy or use the GlobalServiceProvider
+ /// property
+ ///
+ protected ServiceProvider SiteServiceProvider
+ {
+ get
+ {
+ ThreadHelper.ThrowIfNotOnUIThread();
+ if (serviceProvider == null)
+ {
+ var sp = site as Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
+ serviceProvider = new ServiceProvider(sp);
+ }
+
+ return serviceProvider;
+ }
+ }
+
+ ///
+ /// Provides a wrapper on the global service provider for Visual Studio
+ ///
+ protected ServiceProvider GlobalServiceProvider
+ {
+ get
+ {
+ ThreadHelper.ThrowIfNotOnUIThread();
+ if (globalProvider == null)
+ {
+ var siteServiceProvider = SiteServiceProvider;
+ if (siteServiceProvider != null && siteServiceProvider.GetService(typeof(IVsHierarchy)) is IVsHierarchy vsHierarchy)
+ {
+ ErrorHandler.ThrowOnFailure(vsHierarchy.GetSite(out var ppSP));
+ if (ppSP != null)
+ {
+ globalProvider = new ServiceProvider(ppSP);
+ }
+ }
+ }
+
+ return globalProvider;
+ }
+ }
+
+ ///
+ /// SetSite method of IOleObjectWithSite
+ ///
+ /// site for this object to use
+ public virtual void SetSite(object pUnkSite)
+ {
+ site = pUnkSite;
+ serviceProvider = null;
+ }
+
+ ///
+ /// GetSite method of IOleObjectWithSite
+ ///
+ /// interface to get
+ /// array in which to stuff return value
+ public virtual void GetSite(ref Guid riid, out IntPtr ppvSite)
+ {
+ if (site == null)
+ {
+ Marshal.ThrowExceptionForHR(-2147467259);
+ }
+
+ IntPtr iUnknownForObject = Marshal.GetIUnknownForObject(site);
+ Marshal.QueryInterface(iUnknownForObject, ref riid, out var ppv);
+ if (ppv == IntPtr.Zero)
+ {
+ Marshal.ThrowExceptionForHR(-2147467262);
+ }
+
+ ppvSite = ppv;
+ }
+
+ protected object GetService(Guid service) => SiteServiceProvider?.GetService(service);
+
+ protected object GetService(Type service) => SiteServiceProvider?.GetService(service);
+
+ int IVsSingleFileGenerator.DefaultExtension(out string pbstrDefaultExtension)
+ {
+ pbstrDefaultExtension = GetDefaultExtension();
+ return VSConstants.S_OK;
+ }
+
+ int IVsSingleFileGenerator.Generate(string wszInputFilePath, string bstrInputFileContents, string wszDefaultNamespace, IntPtr[] rgbOutputFileContents, out uint pcbOutput, IVsGeneratorProgress pGenerateProgress)
+ {
+ try
+ {
+ IntPtr outputFileContents = IntPtr.Zero;
+ GenerateInternal(wszInputFilePath, bstrInputFileContents, wszDefaultNamespace, out outputFileContents, out var output, pGenerateProgress);
+ rgbOutputFileContents[0] = outputFileContents;
+ pcbOutput = (uint)output;
+ }
+ catch
+ {
+ pcbOutput = 0u;
+ rgbOutputFileContents[0] = IntPtr.Zero;
+ return VSConstants.E_FAIL;
+ }
+
+ return VSConstants.S_OK;
+ }
+
+ protected abstract string GetDefaultExtension();
+
+ private void GenerateInternal(
+ string inputFilePath, string inputFileContents, string defaultNamespace,
+ out IntPtr outputFileContents, out int output, IVsGeneratorProgress pGenerateProgress)
+ {
+ if (inputFileContents == null)
+ {
+ throw new ArgumentNullException(nameof(inputFileContents));
+ }
+
+ var array = ThreadHelper.JoinableTaskFactory.Run(
+ async () => await GenerateCodeAsync(inputFilePath, inputFileContents, defaultNamespace, pGenerateProgress));
+ if (array == null)
+ {
+ outputFileContents = IntPtr.Zero;
+ output = 0;
+ }
+ else
+ {
+ output = array.Length;
+ outputFileContents = Marshal.AllocCoTaskMem(output);
+ Marshal.Copy(array, 0, outputFileContents, output);
+ }
+ }
+
+ async Task IVsSingleFileGeneratorAsync.GetDefaultExtensionAsync(CancellationToken cancellationToken)
+ => GetDefaultExtension();
+
+ async Task IVsSingleFileGeneratorAsync.GenerateAsync(string inputFilePath, string inputFileContents, string defaultNamespace, Stream outputStream, IVsGeneratorProgress generatorProgress, CancellationToken cancellationToken)
+ {
+ var resultBytes = await GenerateCodeAsync(inputFilePath, inputFileContents, defaultNamespace, generatorProgress, cancellationToken);
+ if (resultBytes != null)
+ {
+ await outputStream.WriteAsync(resultBytes, 0, resultBytes.Length);
+ }
+
+ return GeneratorResult.Success;
+ }
+
+ protected abstract Task GenerateCodeAsync(
+ string inputFilePath, string inputFileContents, string defaultNamespace,
+ IVsGeneratorProgress generateProgress, CancellationToken cancellationToken = default);
+
+ ~BaseCodeGeneratorWithSiteAsync() => Dispose(disposing: false);
+
+ public void Dispose()
+ {
+ Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// In derived classes, override Dispose(bool disposing) and make sure you call this method
+ /// even in case of exceptions (in finally).
+ ///
+ /// True to release managed resources; false from finalizer.
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed) return;
+
+ try
+ {
+ if (disposing)
+ {
+ serviceProvider?.Dispose();
+ serviceProvider = null;
+
+ globalProvider?.Dispose();
+ globalProvider = null;
+ }
+ }
+ finally
+ {
+ _disposed = true;
+ }
+ }
+}
diff --git a/AsyncSingleFileGenerator/src/Generators/MinifyGenerator.cs b/AsyncSingleFileGenerator/src/Generators/MinifyGenerator.cs
new file mode 100644
index 00000000..bb6b2b89
--- /dev/null
+++ b/AsyncSingleFileGenerator/src/Generators/MinifyGenerator.cs
@@ -0,0 +1,70 @@
+namespace AsyncSingleFileGeneratorSample;
+
+using EnvDTE;
+using Microsoft.VisualStudio.Shell.Interop;
+using Microsoft.VisualStudio.TextTemplating.VSHost;
+using NUglify;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+///
+/// Most of the magic is done in the BaseCodeGeneratorWithSiteAsync class, don't forget to check it out!
+///
+[Guid("AE47AF68-696D-491E-B536-32F859E0B988")]
+public sealed class MinifyCodeGenerator : BaseCodeGeneratorWithSiteAsync
+{
+ public const string Name = nameof(MinifyCodeGenerator);
+ public const string Description = "Generates a minified version of JavaScript, CSS and HTML files files.";
+
+ protected override string GetDefaultExtension()
+ {
+ var item = GetService(typeof(ProjectItem)) as ProjectItem;
+ return ".min" + Path.GetExtension(item?.FileNames[1]);
+ }
+
+ protected override async Task GenerateCodeAsync(
+ string inputFileName, string inputFileContent, string defaultNamespace,
+ IVsGeneratorProgress generateProgress, CancellationToken cancellationToken = default)
+ {
+ if (string.IsNullOrWhiteSpace(inputFileContent)) { return null; }
+
+ var result = await MinifyAsync(inputFileName, inputFileContent);
+
+ if (result.HasErrors)
+ {
+ return Encoding.UTF8.GetBytes("// Source file contains errors");
+ }
+ else
+ {
+ return Encoding.UTF8.GetBytes(result.Code);
+ }
+ }
+
+ private static Task MinifyAsync(string inputFileName, string inputFileContent)
+ {
+ // Note! Uglify does not have async methods, so we run it in a Task to avoid blocking.
+
+ return Task.Run(() =>
+ {
+ string ext = Path.GetExtension(inputFileName).ToLowerInvariant();
+
+ switch (ext)
+ {
+ case ".js":
+ return Uglify.Js(inputFileContent);
+ case ".css":
+ return Uglify.Css(inputFileContent);
+ case ".htm":
+ case ".html":
+ return Uglify.Html(inputFileContent);
+ }
+
+ return new UglifyResult(inputFileContent, new List());
+ });
+ }
+}
\ No newline at end of file
diff --git a/AsyncSingleFileGenerator/src/Properties/AssemblyInfo.cs b/AsyncSingleFileGenerator/src/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..ddaa6ae2
--- /dev/null
+++ b/AsyncSingleFileGenerator/src/Properties/AssemblyInfo.cs
@@ -0,0 +1,16 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("Async Single File Generator Sample")]
+[assembly: AssemblyDescription("Shows how to implement an async single file generator for Visual Studio")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Reza Niroomand")]
+[assembly: AssemblyProduct("Async Single File Generator Sample")]
+[assembly: AssemblyCopyright("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AsyncSingleFileGenerator/src/Resources/ApplyCustomToolPackage.ico b/AsyncSingleFileGenerator/src/Resources/ApplyCustomToolPackage.ico
new file mode 100644
index 00000000..d323b07f
Binary files /dev/null and b/AsyncSingleFileGenerator/src/Resources/ApplyCustomToolPackage.ico differ
diff --git a/AsyncSingleFileGenerator/src/Resources/Icon128x128.png b/AsyncSingleFileGenerator/src/Resources/Icon128x128.png
new file mode 100644
index 00000000..76e4085f
Binary files /dev/null and b/AsyncSingleFileGenerator/src/Resources/Icon128x128.png differ
diff --git a/AsyncSingleFileGenerator/src/SingleFileGeneratorSample.csproj b/AsyncSingleFileGenerator/src/SingleFileGeneratorSample.csproj
new file mode 100644
index 00000000..fc2fe289
--- /dev/null
+++ b/AsyncSingleFileGenerator/src/SingleFileGeneratorSample.csproj
@@ -0,0 +1,128 @@
+
+
+
+ 15.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+ true
+ 10.0
+
+
+
+ false
+
+
+
+
+
+
+
+ Debug
+ AnyCPU
+ 2.0
+ {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ {154F8BFA-6D1D-4FEF-95AC-95D4222B34DA}
+ Library
+ Properties
+ AsyncSingleFileGeneratorSample
+ AsyncSingleFileGeneratorSample
+ v4.8
+ true
+ true
+ true
+ true
+ true
+ false
+ Program
+ $(DevEnvDir)devenv.exe
+ /rootsuffix Exp
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+ Designer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Menus.ctmenu
+
+
+
+
+ true
+ VSPackage
+
+
+
+
+
+ true
+ Always
+
+
+
+
+ 17.14.40265
+
+
+ 17.14.2120
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ 13.0.4
+
+
+ 1.21.17
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AsyncSingleFileGenerator/src/VSCommandTable.vsct b/AsyncSingleFileGenerator/src/VSCommandTable.vsct
new file mode 100644
index 00000000..b7494fec
--- /dev/null
+++ b/AsyncSingleFileGenerator/src/VSCommandTable.vsct
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AsyncSingleFileGenerator/src/VSPackage.cs b/AsyncSingleFileGenerator/src/VSPackage.cs
new file mode 100644
index 00000000..6aac96e3
--- /dev/null
+++ b/AsyncSingleFileGenerator/src/VSPackage.cs
@@ -0,0 +1,27 @@
+namespace AsyncSingleFileGeneratorSample;
+
+using System;
+using System.Runtime.InteropServices;
+using System.Threading;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.TextTemplating.VSHost;
+using Task = System.Threading.Tasks.Task;
+
+[Guid("2e927fa3-8684-47fc-9674-0046499860d3")] // Must match the GUID in the .vsct file
+[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
+[InstalledProductRegistration("Single File Generator Sample", "", "1.0")]
+[ProvideMenuResource("Menus.ctmenu", 1)]
+[ProvideCodeGenerator(typeof(MinifyCodeGenerator), MinifyCodeGenerator.Name, MinifyCodeGenerator.Description, true)]
+[ProvideUIContextRule("69760bd3-80f0-4901-818d-c4656aaa08e9", // Must match the GUID in the .vsct file
+ name: "UI Context",
+ expression: "js | css | html", // This will make the button only show on .js, .css and .htm(l) files
+ termNames: new[] { "js", "css", "html" },
+ termValues: new[] { "HierSingleSelectionName:.js$", "HierSingleSelectionName:.css$", "HierSingleSelectionName:.html?$" })]
+public sealed class VSPackage : AsyncPackage
+{
+ protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress)
+ {
+ await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
+ await ApplyCustomTool.InitializeAsync(this);
+ }
+}
diff --git a/AsyncSingleFileGenerator/src/VSPackage.resx b/AsyncSingleFileGenerator/src/VSPackage.resx
new file mode 100644
index 00000000..8d8bd213
--- /dev/null
+++ b/AsyncSingleFileGenerator/src/VSPackage.resx
@@ -0,0 +1,140 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ ApplyCustomTool Extension
+
+
+ ApplyCustomTool Visual Studio Extension Detailed Info
+
+
+ Resources\ApplyCustomToolPackage.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
\ No newline at end of file
diff --git a/AsyncSingleFileGenerator/src/source.extension.vsixmanifest b/AsyncSingleFileGenerator/src/source.extension.vsixmanifest
new file mode 100644
index 00000000..d790c55a
--- /dev/null
+++ b/AsyncSingleFileGenerator/src/source.extension.vsixmanifest
@@ -0,0 +1,23 @@
+
+
+
+
+ Single File Generator Sample
+ Shows how to implement a single file generator for Visual Studio
+ Resources\Icon128x128.png
+ Resources\Icon128x128.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+