diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/Properties/Resources.Designer.cs b/WelsonJS.Toolkit/WelsonJS.Launcher/Properties/Resources.Designer.cs index 4d367f4..e40782d 100644 --- a/WelsonJS.Toolkit/WelsonJS.Launcher/Properties/Resources.Designer.cs +++ b/WelsonJS.Toolkit/WelsonJS.Launcher/Properties/Resources.Designer.cs @@ -60,6 +60,15 @@ namespace WelsonJS.Launcher.Properties { } } + /// + /// https://ajax.aspnetcdn.com/과(와) 유사한 지역화된 문자열을 찾습니다. + /// + internal static string AspNetCdnPrefix { + get { + return ResourceManager.GetString("AspNetCdnPrefix", resourceCulture); + } + } + /// /// 과(와) 유사한 지역화된 문자열을 찾습니다. /// @@ -132,6 +141,24 @@ namespace WelsonJS.Launcher.Properties { } } + /// + /// https://esm.run/과(와) 유사한 지역화된 문자열을 찾습니다. + /// + internal static string EsmRunPrefix { + get { + return ResourceManager.GetString("EsmRunPrefix", resourceCulture); + } + } + + /// + /// https://esm.sh/과(와) 유사한 지역화된 문자열을 찾습니다. + /// + internal static string EsmShPrefix { + get { + return ResourceManager.GetString("EsmShPrefix", resourceCulture); + } + } + /// /// (아이콘)과(와) 유사한 System.Drawing.Icon 형식의 지역화된 리소스를 찾습니다. /// @@ -142,6 +169,15 @@ namespace WelsonJS.Launcher.Properties { } } + /// + /// https://ajax.googleapis.com/과(와) 유사한 지역화된 문자열을 찾습니다. + /// + internal static string GoogleApisPrefix { + get { + return ResourceManager.GetString("GoogleApisPrefix", resourceCulture); + } + } + /// /// System.Drawing.Bitmap 형식의 지역화된 리소스를 찾습니다. /// @@ -232,6 +268,42 @@ namespace WelsonJS.Launcher.Properties { } } + /// + /// https://code.jquery.com/과(와) 유사한 지역화된 문자열을 찾습니다. + /// + internal static string JqueryCdnPrefix { + get { + return ResourceManager.GetString("JqueryCdnPrefix", resourceCulture); + } + } + + /// + /// https://cdn.jsdelivr.net/과(와) 유사한 지역화된 문자열을 찾습니다. + /// + internal static string JsDeliverPrefix { + get { + return ResourceManager.GetString("JsDeliverPrefix", resourceCulture); + } + } + + /// + /// https://polyfill-fastly.io/과(와) 유사한 지역화된 문자열을 찾습니다. + /// + internal static string PolyfillPrefix { + get { + return ResourceManager.GetString("PolyfillPrefix", resourceCulture); + } + } + + /// + /// https://raw.githubusercontent.com/과(와) 유사한 지역화된 문자열을 찾습니다. + /// + internal static string RawGitHubPrefix { + get { + return ResourceManager.GetString("RawGitHubPrefix", resourceCulture); + } + } + /// /// https://github.com/gnh1201/welsonjs과(와) 유사한 지역화된 문자열을 찾습니다. /// @@ -250,6 +322,24 @@ namespace WelsonJS.Launcher.Properties { } } + /// + /// https://www.skypack.dev/과(와) 유사한 지역화된 문자열을 찾습니다. + /// + internal static string SkypackPrefix { + get { + return ResourceManager.GetString("SkypackPrefix", resourceCulture); + } + } + + /// + /// https://unpkg.com/과(와) 유사한 지역화된 문자열을 찾습니다. + /// + internal static string UnpkgPrefix { + get { + return ResourceManager.GetString("UnpkgPrefix", resourceCulture); + } + } + /// /// 141.101.82.1과(와) 유사한 지역화된 문자열을 찾습니다. /// diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/Properties/Resources.resx b/WelsonJS.Toolkit/WelsonJS.Launcher/Properties/Resources.resx index bf5af31..5e041d3 100644 --- a/WelsonJS.Toolkit/WelsonJS.Launcher/Properties/Resources.resx +++ b/WelsonJS.Toolkit/WelsonJS.Launcher/Properties/Resources.resx @@ -187,4 +187,34 @@ https://cdnjs.cloudflare.com/ + + https://esm.run/ + + + https://esm.sh/ + + + https://code.jquery.com/ + + + https://cdn.jsdelivr.net/ + + + https://www.skypack.dev/ + + + https://unpkg.com/ + + + https://ajax.aspnetcdn.com/ + + + https://ajax.googleapis.com/ + + + https://polyfill-fastly.io/ + + + https://raw.githubusercontent.com/ + \ No newline at end of file diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceServer.cs b/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceServer.cs index cd21f07..caf1f72 100644 --- a/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceServer.cs +++ b/WelsonJS.Toolkit/WelsonJS.Launcher/ResourceServer.cs @@ -1,12 +1,19 @@ -using System; +// ResourceServer.cs +// A resource server of WelsonJS Editor (WelsonJS.Launcher) +// Namhyeon Go +// https://github.com/gnh1201/welsonjs +// +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Net; using System.Net.Http; using System.Reflection; using System.Security.Cryptography; using System.Text; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; @@ -23,7 +30,25 @@ namespace WelsonJS.Launcher private string _resourceName; private List _tools = new List(); private readonly HttpClient _httpClient = new HttpClient(); - private readonly string _defaultMimeType = "application/octet-stream"; + private static readonly string _defaultMimeType = "application/octet-stream"; + private static readonly Regex _nodePackageRegex = new Regex(@"^[^/@]+@[^/]+/", RegexOptions.Compiled); + private static readonly List CDN_PREFIXES = new List { + new[] { "ajax/libs/" }, + new[] { "npm/", "gh/", "wp/" }, + new[] { "jquery/" }, + new[] { "polyfill/" }, + new[] { "ajax/" }, // https://learn.microsoft.com/en-us/aspnet/ajax/cdn/overview + new[] { "raw/gh/"} + }; + private enum CDN_TYPES: int + { + Cloudflare = 0, + JsDeliver = 1, + Jquery = 2, + Polyfill = 3, + Microsoft = 4, + GitHub = 5 + }; public ResourceServer(string prefix, string resourceName) { @@ -182,17 +207,55 @@ namespace WelsonJS.Launcher } } - // use the cdn-js - if (path.StartsWith("ajax/libs/")) + // use CDN sources + if (await TryServeFromCdn(context, path)) { - if (await ServeBlob(context, path, Program.GetAppConfig("CdnJsPrefix"))) { - return true; - } + return true; + } + } + + return false; + } + + private async Task TryServeFromCdn(HttpListenerContext context, string path) + { + bool isNodePackageExpression = _nodePackageRegex.IsMatch(path); + bool isPrefixMatched(CDN_TYPES type) + { + if (CDN_PREFIXES[(int)type].Any(prefix => path.StartsWith(prefix))) + { + return true; } - // use the blob stroage - if (await ServeBlob(context, path, Program.GetAppConfig("BlobStoragePrefix"))) { - return true; + return false; + } + + var sources = new (bool isMatch, string configKey, Func transform)[] + { + (isPrefixMatched(CDN_TYPES.Cloudflare), "CdnJsPrefix", p => p), // Libraries from Cloudflare + (isPrefixMatched(CDN_TYPES.Cloudflare), "GoogleApisPrefix", p => p), // Libraries from Google + (isNodePackageExpression, "UnpkgPrefix", p => p), + (isNodePackageExpression, "SkypackPrefix", p => p), + (isNodePackageExpression, "EsmShPrefix", p => p), + (isNodePackageExpression, "EsmRunPrefix", p => p), + (isPrefixMatched(CDN_TYPES.JsDeliver), "JsDeliverPrefix", p => p), + (isPrefixMatched(CDN_TYPES.Jquery), "JqueryCdnPrefix", p => p.Substring("jquery/".Length)), + (isPrefixMatched(CDN_TYPES.Polyfill), "CdnJsPrefix", p => p), // polyfill.js from Cloudflare + (isPrefixMatched(CDN_TYPES.Polyfill), "PolyfillPrefix", p => p.Substring("polyfill/".Length)), // polyfill.js from Fastly + (isPrefixMatched(CDN_TYPES.Microsoft), "AspNetCdnPrefix", p => p), // Libraries from Microsoft + (isPrefixMatched(CDN_TYPES.GitHub), "RawGitHubPrefix", p => p.Substring("raw/gh/".Length)), + (true, "BlobStoragePrefix", p => p) // fallback + }; + + foreach (var (isMatch, configKey, transform) in sources) + { + if (isMatch) + { + string prefix = Program.GetAppConfig(configKey); + if (await ServeBlob(context, transform(path), prefix)) + { + return true; + } } } diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/app.config b/WelsonJS.Toolkit/WelsonJS.Launcher/app.config index 729e7ad..4bd67b0 100644 --- a/WelsonJS.Toolkit/WelsonJS.Launcher/app.config +++ b/WelsonJS.Toolkit/WelsonJS.Launcher/app.config @@ -14,6 +14,15 @@ + + + + + + + + +