From 9fd5322a3347e81f66b8ff18648f2658b02cdcfb Mon Sep 17 00:00:00 2001
From: Demis Bellot
This behavior provides the optimal UX for developers and end-users as HTML Pages with Exceptions are still rendered well-formed
whilst still being easily able to display a curated error messages for end-users without developers needing to guard against
- executing filters when Exceptions occur.
+ executing methods when Exceptions occur.
- We can also enable this behavior on a per-page basis using the skipExecutingFiltersOnError filter:
+ We can also enable this behavior on a per-page basis using the skipExecutingFiltersOnError method:
- Here we can see that any normal filters after exceptions are never evaluated unless they're specifically for handling Errors.
+ Here we can see that any normal methods after exceptions are never evaluated unless they're specifically for handling Errors.
- We can also specify that we want to continue executing filters in which case you'll need to manually guard filters you want to
- control the execution of using the ifNoError or ifError filters:
+ We can also specify that we want to continue executing methods in which case you'll need to manually guard methods you want to
+ control the execution of using the ifNoError or ifError methods:
- The last Exception thrown are accessible using the lastError* filters:
+ The last Exception thrown are accessible using the lastError* methods:
- We've included a few of the popular Exception Types, filters prefixed with throw always throws the Exceptions below:
+ We've included a few of the popular Exception Types, methods prefixed with throw always throws the Exceptions below:
- These filters will only throw if a condition is met:
+ These methods will only throw if a condition is met:
- The ensureAll* filters assert the state of multiple arguments where it will either throw an Exception unless
+ The ensureAll* methods assert the state of multiple arguments where it will either throw an Exception unless
all the arguments meet the condition or return the Object Dictionary if all conditions are met:
- The ensureAny* filters only requires one of the arguments meet the condition to return the Object Dictionary:
+ The ensureAny* methods only requires one of the arguments meet the condition to return the Object Dictionary:
The Exception Types below are reserved for Exceptions that should never happen, such as incorrect usage of an API where it would've
- resulted in a compile error in C#. When these Exceptions are thrown in a filter or a page they'll immediately short-circuit execution of
+ resulted in a compile error in C#. When these Exceptions are thrown in a method or a page they'll immediately short-circuit execution of
the page and write the Exception details to the output.
- The Fatal Exceptions include:
+ The Fatal Exceptions include:
The OnExpressionException delegate in
Page Formats
- are able to control how Exceptions in filter expressions are rendered where if preferred exceptions can be rendered in-line
- in the filter expression where they occured with:
+ are able to control how Exceptions in template expressions are rendered where if preferred exceptions can be rendered in-line
+ in the template expression where they occured with:
The OnExpressionException can also suppress Exceptions from being displayed by capturing any naked Exception Types registered in
- TemplateConfig.CaptureAndEvaluateExceptionsToNull
- and evaluate the filter expression to null which by default will suppress the following naked Exceptions thrown in filters:
+ TemplateConfig.CaptureAndEvaluateExceptionsToNull
+ and evaluate the template expression to null which by default will suppress the following naked Exceptions thrown in methods:
- In order for your own Filter Exceptions to participate in the above Template Error Handling they'll need to be wrapped in an
- StopFilterExecutionException including both the Template's scope and an optional options object
+ In order for your own Method Exceptions to participate in the above Script Error Handling they'll need to be wrapped in an
+ StopFilterExecutionException including both the Script's scope and an optional options object
which is used to check if the assignError binding was provided so it can automatically populate it with the Exception.
- The easiest way to Implement Exception handling in filters is to call a managed function which catches all Exceptions and
+ The easiest way to Implement Exception handling in methods is to call a managed function which catches all Exceptions and
throws them in a StopFilterExecutionException as seen in OrmLite's
- TemplateDbFilters:
+ DbScripts:
- The overloads are so the filters can be called without specifying any filter options.
+ The overloads are so the methods can be called without specifying any method options.
For more examples of different error handling features and strategies checkout:
- TemplateErrorHandlingTests.cs
+ ErrorHandlingTests.cs
The Parcel WebApp template is maintained in the following directory structure: The Parcel SharpApp template is maintained in the following directory structure: Then to start the ServiceStack Server to host your Web App run: Which will host your App at Which will host your App at To enable even richer functionality, this Web Apps template is also pre-configured with a custom Server project where you can extend your Web App with Plugins where all To enable even richer functionality, this Sharp Apps template is also pre-configured with a custom Server project where you can extend your Web App with Plugins where all This template includes a simple ServerPlugin.cs which contains an Empty This will automatically load any One benefit of creating your APIs with C# ServiceStack Services instead of API Pages is that you can generate TypeScript DTOs with: One benefit of creating your APIs with C# ServiceStack Services instead of API Pages is that you can generate TypeScript DTOs with: Which saves generate DTOs for all your ServiceStack Services in dtos.ts which can then be accessed in your TypeScript source code. Which will bundle and minify all Then to deploy Web Apps you just need to copy the Then to deploy Sharp Apps you just need to copy the Any infrastructure dependencies have also been avoided by using SQLite by default which is
-automatically created an populated on first run if no database exists, or if preferred can be
-changed to use any other popular RDBMS using just config. Any number of users can Sign In via Twitter and publish content under their Twitter Username where only they'll be able to modify their own Content.
@@ -42,7 +42,7 @@ The new.html and edit.html pages shows examples of performing server validation with ServiceStack Templates: The new.html and edit.html pages shows examples of performing server validation with SharpScript: To the /preview.html API Page which just renders and captures any
Template Content its sent and returns the output: This also the preferred way to enable the Metadata Debug Template
+ This also the preferred way to enable the Metadata Debug Inspector
in production, which is only available in
+ SharpScript is a simple, fast, highly versatile and
+ embeddable scripting language for .NET Core and .NET Apps that utilizes a
+ familiar expressive Syntax
+ to enable dynamic scripting of .NET Apps via controlled access to pluggable
+ methods and arguments
+ within a sandbox environment - ensuring scripts are encapsulated and developed using clean, reusable and testable components.
+
- At its core ServiceStack Templates is a simple, fast, and extremely versatile
- dynamic templating language for .NET and .NET Core that utilizes an Expressive Syntax to compose high-level
- functionality using .NET filters and arguments.
+ The language itself a familiar combination of
+ JavaScript expressions contained within Template Expressions popularized in
+ Vue.js filters and
+ Angular's Template Expressions
+ whilst statements adopt Handlebars block helpers syntax. The language is also highly extensible which is pre-configured
+ comprehensive suite of safe methods and blocks by default, but are all registered via its
+ rich plugin functionality which can be completely removed or "shadowed" (aka overridden) where it could be used to form the basis of your
+ own completely different DSL.
+ In addition to the language, SharpScript includes a surrounding ecosystem of rich functionality adapting SharpScript
+ to power a number of fun and exciting scenarios. E.g. Scripts are lazily loaded from its Virtual File System
+ which can be configured to use any of the available Virtual File Systems
+ and is what enables Pure Cloud Apps where entire Sharp Apps could be hosted within
+ your AWS S3 Bucket or Azure Blob Storage.
+
It's small, lightweight footprint and built-in Hot Reloading
provides a fun, clean and productive alternative to MVC Razor that's easily
- integrated into any web framework and runs identically in
- every platform ServiceStack runs on, it can also
- be returned in ASP.NET MVC and ASP.NET MVC Core
+ integrated into any web framework and runs identically in
+ every platform ServiceStack runs on, as well as
+ within ASP.NET MVC and ASP.NET MVC Core
Controllers - in all cases, using the same high-performance implementation
to asynchronously write to a forward-only OutputStream for max performance and maximum potential reuse of your Source Code.
- Templates are lazily loaded and late-bound for Instant Startup, doesn't require any
- pre-compilation, have coupling to any external configuration files, build tools, designer tooling or have
- any special deployment requirements.
- It can be used as a general purpose templating language to enhance any text format and
- includes built-in support for .html.
+ SharpScripts are lazily loaded and late-bound for Instant Startup, doesn't require any
+ pre-compilation, coupling to any external configuration files, build tools, designer tooling or have any special deployment requirements.
+ It can be used as a general purpose scripting or templating language to generate any text format
+ and includes built-in support for .html.
- Templates are evaluated in an Isolated Sandboxed that enables fine-grained control over exactly
- what functionality and instances are available to different Templates. They're pre-configured with a comprehensive suite of safe
- Default Filters which when running in trusted contexts can easily be granted access to
+ Similar to Vue.js Components, Sharp Script's are evaluated in an
+ Encapsulated Sandbox that enables fine-grained control over exactly what functionality and instances
+ are available to different Scripts. They're pre-configured with a comprehensive suite of safe
+ Default Scripts which when running in trusted contexts can easily be granted access to
enhanced functionality.
- Templates are designed to be incrementally adoptable where its initial form is
- ideal for non-programmers,
+ SharpScript is designed to be incrementally adoptable where its initial form is
+ ideal for non-programmers,
that can gradually adopt more power and functionality when needed where they can leverage existing Services or MVC Controllers
to enable an
MVC programming model or have .html pages upgraded to use
@@ -44,48 +77,54 @@
normal .html pages are requested making them a non-invasive alternative whenever advanced functionality is required.
- These qualities opens Templates up to a number of new use-cases that's better suited than Razor for maintaining
+ These qualities opens SharpScripts up to a number of new use-cases that's better suited than Razor for maintaining
content-heavy websites, live documents,
Email Templates and can easily
introspect the state of running .NET Apps where they provide
- valuable insight at a glance with support for
+ valuable insight at a glance with support for
Adhoc querying.
+ One use-case made possible by SharpScript we're extremely excited about is SharpApps - a new approach to
+ dramatically simplify .NET Web App development and provide the most productive development experience possible.
+
- One use-case made possible by Templates we're extremely excited about is Web Apps - a new approach to
- dramatically simplify .NET Web App development and provide the most productive development experience possible whilst maximizing
- reuse and component sharing.
+ SharpApps leverages SharpScript to develop entire content-rich, data-driven websites in a pure live development model
+ without needing to write any C#, compile projects or manually refresh pages.
- Web Apps leverages Templates to develop entire content-rich, data-driven websites without needing to write any C#, compile projects
- or manually refresh pages - resulting in the easiest and fastest way to develop Web Apps in .NET!
+ The integrated development experience extends to end-to-end to provide a seamless installation and deployment experience where the
+ same web dotnet tool used to develop SharpApps is all users need to find and install Apps locally or hosted as a server App
+ where they're easily deployed and hosted on Linux or run within Docker -
+ the overall SharpApps experience results in the easiest and fastest way to develop and deploy Web Apps in .NET!
- Web Apps also enable the development of Pure Cloud Apps where the same Web App
+ SharpApps also enable the development of Pure Cloud Apps where the same SharpApp
can be developed and run entirely on AWS S3 and RDS or Azure Blob Storage and SQL Server by just changing
- the app.settings that's deployed with the pre-compiled Web App Binary.
+ its app.settings.
- Get started learning ServiceStack Templates by going through the Interactive Guide and creating
+ Get started learning SharpScript by going through the Interactive Guide and creating
a Starter Project.
View the source code of this
- Template Pages generated website to see how clean a website built with it is.
+ Sharp Pages website to see how clean a website built with it is.
@@ -95,19 +134,17 @@
- We believe we've only just scratched the surface of what's possible with Templates and we'd love to see what new
- use-cases it can help achieve and help encourage an ecosystem of pluggable and reusable filters, so whilst ServiceStack is a
- dual-licensed platform with a
- commercial license for closed-source projects, the core of Templates is
- being developed in ServiceStack.Common which is an unrestricted
- library that's free for OSS and commercial usage.
+ We believe we've only scratched the surface of what's possible with SharpScript and we'd love to see what new
+ use-cases it can help achieve and help encourage an ecosystem of pluggable and reusable scripts, SharpScript is
+ being developed in ServiceStack.Common which is a
+ free library for commercial or non-commercial use.
- ServiceStack's existing free-quota restrictions
+ ServiceStack's existing free-quota restrictions
only applies if you're using OrmLite,
ServiceStack.Redis or exceed the allowed free-quota of
ServiceStack Services.
diff --git a/src/wwwroot/linq/index.html b/src/wwwroot/linq/index.html
index 37066fe..3058723 100644
--- a/src/wwwroot/linq/index.html
+++ b/src/wwwroot/linq/index.html
@@ -4,13 +4,13 @@
-->
- All LINQ Examples are executed using the same TemplateContext instance below:
+ All LINQ Examples are executed using the same ScriptContext instance below:
- The same qualities that make Templates great at
+ The same qualities that make SharpScript great at
querying State of a running .NET App
also makes it excel at executing adhoc queries against providers which allow free text queries like
- OrmLite's Database Filters which enables access to its
+ OrmLite's Database Scripts which enables access to its
Dynamic Result Set APIs:
- To access OrmLite's Database Filters install the
+ To access OrmLite's Database Scripts install the
OrmLite NuGet package for your RDBMS then add the
- TemplateDbFiltersAsync
- to your TemplateContext and register its required IDbConnectionFactory dependency in its IOC, e.g. for SQL Server:
+ DbScriptsAsync
+ to your ScriptContext and register its required IDbConnectionFactory dependency in its IOC, e.g. for SQL Server:
- If you're exposing filters enabling a free text API against a production database it should never be accessible by untrusted parties
+ If you're exposing script methods enabling a free text API against a production database it should never be accessible by untrusted parties
so you'll want to at a minimum ensure Services are protected with the [Authenticate] attribute so it's only available to
- Authenticated Users and
+ Authenticated Users and
ideally configure it to use a Read Only connection, e.g. for SQLite:
The database queried above was populated in the
- AppHost
+ AppHost
where it re-uses the LINQ data sources to create and populate an In Memory SQLite database on Startup:
- In its simplest form Templates is just plain HTML marked up with variable place holders, coupled with
- partials just being HTML pages themselves and the intuitive Cascading Layout Selection
- and using Templates becomes a natural solution for building complete websites without any code.
+ In its simplest form SharpScript is just plain HTML marked up with variable place holders, coupled with
+ partials just being HTML pages themselves and the intuitive Cascading Layout Selection
+ and using SharpScript becomes a natural solution for building complete websites without any code.
- The flexibility of Templates allows for several different solutions for generating websites:
+ The flexibility of SharpScript allows for several different solutions for generating websites:
- You won't need to specify a custom layout as Templates will automatically select the closest layout from the
- page at:
- /rockstar-files/dead/_layout.html.
+ You won't need to specify a custom layout as Sharp Pages will automatically select the closest layout from the page at:
+ /rockstar-files/dead/_layout.html.
You'll also no longer need to maintain your Layout pages and partials in /Views/Shared separate from your views as they
all get to all live cohesively in the same logical folder structure.
The declarative {{ pass: page }} is used to embed a page in a layout instead of the imperative @RenderBody().
Likewise the syntax for partials changes to {{ pass: "menu-alive" | partial }} from @Html.Partial("MenuAlive").
- Templates also alleviates the need for bespoke partials like @Html.PartialMarkdown("Content") as it can instead leverage the
+ Sharp Pages also alleviates the need for bespoke partials like @Html.PartialMarkdown("Content") as it can instead leverage the
flexibility of chaining existing filters to achieve the same result like
{{ pass: "content.md" | includeFile | markdown }}.
- To get a feel for what an equivalent Razor Website looks like compared to Templates checkout:
+ To get a feel for what an equivalent Razor Website looks like compared to Sharp Pages checkout:
A useful alternative to embedding static file content in pages is to source the content from a external url. Using the
- includeUrlWithCache filter this is easy to do with great
+ includeUrlWithCache method this is easy to do with great
performance where you can embed remote url content, cache it for 1 minute and convert from markdown with:
- As seen in /grohl-url/index.html
+ As seen in /grohl-url/index.html
which is viewable at:
- Since Templates are intuitive and approachable to non-programmers it's useful in several Business Activities that are better
+ Since SharpScript is intuitive and approachable to non-programmers it's useful in several Business Activities that are better
served by non-technical Business employees like Marketers, Designers, Copywriters, etc. Generating and Previewing email
templates are one example of this:
The source code for this
- email-templates.html
+ email-templates.html
page shows the client preview itself is just using
Bootstrap Tabs that only uses this custom javascript:
- The ease-of-use, performance, no precompilation, interactivity and sandbox features of Templates makes it useful for a
+ The ease-of-use, performance, no precompilation, interactivity and sandbox features of SharpScript makes it useful for a
wide range of use cases. To help stimulate some potential ideas we've included some example use cases below:
Build entire content-rich and data-driven .NET Websites in real-time for each major server platform without compilation.
- Use Templates built-in functionality and live interactivity experience to author and preview smart documents in real-time.
+ Use SharpScript's built-in functionality and live interactivity experience to author and preview smart documents in real-time.
- Use Templates to execute adhoc queries on the state of a running .NET App.
+ Use SharpScript to execute adhoc queries on the state of a running .NET App.
- Since templates are executable at runtime without precompilation it's a great tool for running
+ Since SharpScript's are executable at runtime without precompilation it's a great tool for running
live queries to inspect the state of a running .NET App within a controlled window sandbox.
Here's an example of querying a Server's state:
- To implement IntrospectStateServices.cs
- we created a separate Service using a new TemplateContext instance with a custom set of filters which just exposes the APIs
+ To implement IntrospectStateServices.cs
+ we created a separate Service using a new ScriptContext instance with a custom set of filters which just exposes the APIs
we want to be able to query:
Then to implement the
- Client UI
+ Client UI
we just used a FORM containing Bootstrap Tabs that only uses this custom javascript:
Which calls the generic ajaxPreview jQuery plugin in
- default.js
+ default.js
to make an ajax request on every text box change.
As this feature is an extremely useful way to inspect the state of a remote .NET or .NET Core App it's an embedded feature in
- ServiceStack which is automatically registered in DebugMode
+ ServiceStack which is automatically registered in DebugMode
which can optionally be made available to everyone with:
Live Demo: blog.web-app.io
- Whilst the above server-generated HTML Redis UI shows how you can easily develop traditional Web Apps using SharpScript, we've also
+ Whilst the above server-generated HTML Redis UI shows how you can easily develop traditional Web Apps using #Script, we've also
rewritten the Redis UI as a Single Page App which is the more suitable choice for an App like this as it provides a more
optimal and responsive UX by only loading the HTML page once on Startup then utilizes Ajax to only download and update
the incremental parts of the App's UI that needs changing.
@@ -380,7 +380,7 @@
- SharpScript also provides a great development experience for Single Page Apps which for the most part gets out of your way letting you
+ #Script also provides a great development experience for Single Page Apps which for the most part gets out of your way letting you
develop the Single Page App as if it were a static .html file, but also benefits from the flexibility of a
dynamic web page when needed.
Whilst most of index.html is a static Vue
- app, SharpScript is leveraged to generate the body of the <redis-info/> Component on the initial home page render:
+ app, #Script is leveraged to generate the body of the <redis-info/> Component on the initial home page render:
- Another area SharpScript is used is to handle the HTTP POST where it calls the redisChangeConnection filter to change
+ Another area #Script is used is to handle the HTTP POST where it calls the redisChangeConnection filter to change
the current Redis connection before rendering the
connection-info.html partial with the
current connection info:
@@ -511,7 +511,7 @@
- As SharpScript is unable to use a Typed ORM like OrmLite
- to hide the nuances of each database, we need to be a bit more diligent in SharpScript to use parameterized SQL that works across
+ As #Script is unable to use a Typed ORM like OrmLite
+ to hide the nuances of each database, we need to be a bit more diligent in #Script to use parameterized SQL that works across
multiple databases by using the
sql* DB Filters to avoid using RDBMS-specific
SQL syntax. The
@@ -979,7 +979,7 @@
- One of the most popular use-cases for a high-performance and versatile scripting language like SharpScript is as a server-side
+ One of the most popular use-cases for a high-performance and versatile scripting language like #Script is as a server-side
HTML Sharp Pages for .NET Web Applications where it can provide a simpler, cleaner and portable alternative than Razor and
Razor Pages in ASP.NET and ASP.NET Core Web Apps.
- The beauty of SharpScript working natively with ServiceStack is that it runs everywhere ServiceStack does
- which is in all major .NET Server Platforms. That is, your same SharpScript-based Web Application is able to use
- the same SharpScript implementation, "flavour" and feature-set and is portable across whichever platform you choose to host it on:
+ The beauty of #Script working natively with ServiceStack is that it runs everywhere ServiceStack does
+ which is in all major .NET Server Platforms. That is, your same #Script-based Web Application is able to use
+ the same #Script implementation, "flavour" and feature-set and is portable across whichever platform you choose to host it on:
- You can ignore SharpScript from evaluating static .html files with the following page arguments:
+ You can ignore #Script from evaluating static .html files with the following page arguments:
- SharpScript aims to be a familiar and expressive dynamic language for scripting .NET Apps, that is optimal
+ #Script aims to be a familiar and expressive dynamic language for scripting .NET Apps, that is optimal
at generating text, especially HTML where it's pre-configured with the
HTML Page Format and HTML Scripts but
unlike C#'s Razor can support multiple pluggable page formats which can be used for generating any kind of text.
- For maximum familiarity SharpScript uses JavaScript Expressions
+ For maximum familiarity #Script uses JavaScript Expressions
that for increased readability and expressiveness supports being used within Template Expressions
which like Vue.js filters,
and Angular's Template Expressions
lets you use the | pipe operator to chain the return values of methods into subsequent methods from left-to-right.
- For statements SharpScript adopts the familiar Handlebars-like
+ For statements #Script adopts the familiar Handlebars-like
Script Blocks that is also popular among HTML templating engines.
- Effectively SharpScript lets you use the same familiar JS language for server HTML rendering as you would do
+ Effectively #Script lets you use the same familiar JS language for server HTML rendering as you would do
in client-side rendering of Single Page Apps despite it binding natively to C# objects and calling C# methods behind-the-scenes.
- As string expressions are a prevalent in SharpScript, we've also given them special wrist-friendly syntax where you
+ As string expressions are a prevalent in #Script, we've also given them special wrist-friendly syntax where you
can add a colon at the end of the method name which says to treat the following characters up until the end of the
line or mustache expression as a string, trim it and convert '{' and '}' chars into mustaches. With this syntax
you can write:
diff --git a/src/wwwroot/gfm/sharp-apis/04.html b/src/wwwroot/gfm/sharp-apis/04.html
index d9ac1ea..744e43e 100644
--- a/src/wwwroot/gfm/sharp-apis/04.html
+++ b/src/wwwroot/gfm/sharp-apis/04.html
@@ -45,7 +45,7 @@ The preview API above is what provides the new Blog Web App's Live Preview feature where it will render any
-SharpScript provided in the content Query String or HTTP Post Form Data, e.g: Any other unknown arguments like 'X-Powered-By' are returned as HTTP Response Headers. Returning the Using the explicit The new.html and edit.html pages shows examples of performing server validation with SharpScript: The new.html and edit.html pages shows examples of performing server validation with #Script:
@@ -19,7 +19,7 @@ string render(string PathInfo, Dictionary
Unhandled Exceptions Behavior
Controlling Unhandled Exception Behavior
{{ 'After Exception' }}
{{ htmlErrorMessage }}` }) }}
Continue Executing Filters on Unhandled Exceptions
+Continue Executing Methods on Unhandled Exceptions
{{ 'After Exception' }}
Accessing Page Exceptions
Accessing Page Exceptions
Throwing Exceptions
-
@@ -181,19 +181,19 @@ Filter
+ Method
Exception
Throwing Exceptions
- You can extend this list with your own custom filters, see the
- Error Handling Filters
+ You can extend this list with your own custom methods, see the
+ Error Handling Methods
for examples.
@@ -150,11 +150,11 @@
-
@@ -227,7 +227,7 @@ Filter
+ Method
Exception
Throwing Exceptions
Ensure Argument Helpers
Ensure Argument Helpers
{{ ex | htmlErrorMessage }}` }) }}
Fatal Exceptions
@@ -267,16 +267,16 @@
Rendering Exceptions
@@ -284,29 +284,29 @@
-Rendering Exceptions
Implementing Filter Exceptions
+Implementing Method Exceptions
/app - Your Web App's published source code and any plugins
-$ npm run server
http://localhost:5000 which in debug mode will enable hot reloading
+http://localhost:5000 which in debug mode will enable hot reloading
which will automatically reload your web page as it detects any file changes made by parcel.
server
-Plugins, Services, Filters, etc are automatically wired and made available to your Web App.Plugins, Services, Filters, etc are automatically wired and made available to your Web App.ServerPlugin and Hello Service:public class ServerPlugin : IPlugin
{
@@ -59,7 +59,7 @@
$ npm run server
Plugins, Services, Filters, etc and make them available to your Web App.$ npm run dtos
$ npm run build
.css, .js and .html assets and publish to /app/wwwroot./app and /web folders to any server with .NET Core 2.1 runtime installed.
-The Deploying Web Apps docs./app and /web folders to any server with .NET Core 2.1 runtime installed.
+The Deploying Sharp Apps docs.
Multi User Blogging Platform
the database instead of
localStorage.
Server Validation
-public void Configure(Container container)
{
- Plugins.Add(new TemplatePagesFeature());
+ Plugins.Add(new SharpPagesFeature());
}
Plugins.Add(new SharpPagesFeature { HtmlExtension = "htm" });
<!--
layout: mobile-layout
-->
<!--
layout: templates/mobile-layout
-->
public object Any(MyRequest request)
+{
+ ...
+ return new HttpResult(response)
+ {
+ View = "CustomPage",
+ Template = "_custom-layout",
+ };
+}
Plugins.Add(new TemplatePagesFeature {
+
Plugins.Add(new SharpPagesFeature {
TemplatesAdminRole = RoleNames.AllowAnyUser, // Allow any Authenticated User to call /templates/admin
//TemplatesAdminRole = RoleNames.AllowAnon, // Allow anyone
//TemplatesAdminRole = null, // Do not register /templates/admin service
});
DebugMode and Admin Role by default:Plugins.Add(new TemplatePagesFeature {
+
Plugins.Add(new SharpPagesFeature {
MetadataDebugAdminRole = RoleNames.Admin, // Only allow Admin users to call /metadata/debug
//MetadataDebugAdminRole = RoleNames.AllowAnyUser, // Allow Authenticated Users
//MetadataDebugAdminRole = RoleNames.AllowAnon, // Allow anyone
diff --git a/src/wwwroot/gfm/view-engine/11.md b/src/wwwroot/gfm/sharp-pages/11.md
similarity index 77%
rename from src/wwwroot/gfm/view-engine/11.md
rename to src/wwwroot/gfm/sharp-pages/11.md
index 53b5d33..b729a33 100644
--- a/src/wwwroot/gfm/view-engine/11.md
+++ b/src/wwwroot/gfm/sharp-pages/11.md
@@ -1,19 +1,19 @@
```csharp
-Plugins.Add(new TemplatePagesFeature {
+Plugins.Add(new SharpPagesFeature {
TemplatesAdminRole = RoleNames.AllowAnyUser, // Allow any Authenticated User to call /templates/admin
//TemplatesAdminRole = RoleNames.AllowAnon, // Allow anyone
//TemplatesAdminRole = null, // Do not register /templates/admin service
});
```
-This also the preferred way to enable the [Metadata Debug Template](https://docs.servicestack.net/debugging#metadata-debug-template)
+This also the preferred way to enable the [Metadata Debug Inspector](https://docs.servicestack.net/debugging#metadata-debug-template)
in production, which is only available in `DebugMode` and **Admin** Role by default:
```csharp
-Plugins.Add(new TemplatePagesFeature {
+Plugins.Add(new SharpPagesFeature {
MetadataDebugAdminRole = RoleNames.Admin, // Only allow Admin users to call /metadata/debug
//MetadataDebugAdminRole = RoleNames.AllowAnyUser, // Allow Authenticated Users
//MetadataDebugAdminRole = RoleNames.AllowAnon, // Allow anyone
//MetadataDebugAdminRole = null, // Default. Do not register /metadata/debug service
});
-```
+```
\ No newline at end of file
diff --git a/src/wwwroot/gfm/syntax/01.html b/src/wwwroot/gfm/syntax/01.html
index 0c067f1..70d65af 100644
--- a/src/wwwroot/gfm/syntax/01.html
+++ b/src/wwwroot/gfm/syntax/01.html
@@ -1,2 +1,2 @@
-
public string upper(string text) => text?.ToUpper();
public string upper(string text) => text?.ToUpper();
public string substring(string text, int startIndex) => text.SafeSubstring(startIndex);
-public string padRight(string text, int totalWidth, char padChar) => text?.PadRight(totalWidth, padChar);
public string substring(string text, int startIndex) => text.SafeSubstring(startIndex);
+public string padRight(string text, int totalWidth, char padChar) => text?.PadRight(totalWidth, padChar);
public class MarkdownPageFormat : PageFormat
+{
+ private static readonly MarkdownDeep.Markdown markdown = new MarkdownDeep.Markdown();
+
+ public MarkdownPageFormat()
+ {
+ Extension = "md";
+ ContentType = MimeTypes.MarkdownText;
+ }
+
+ public static async Task<Stream> TransformToHtml(Stream markdownStream)
+ {
+ using (var reader = new StreamReader(markdownStream))
+ {
+ var md = await reader.ReadToEndAsync();
+ var html = markdown.Transform(md);
+ return MemoryStreamFactory.GetStream(html.ToUtf8Bytes());
+ }
+ }
+}
var context = new TemplateContext {
- PageFormats = { new MarkdownPageFormat() }
+
var context = new ScriptContext {
+ PageFormats = { new MarkdownPageFormat() }
}.Init();
-context.VirtualFiles.WriteFile("_layout.md", @"
-The Header
-
-{{ page }}");
+context.VirtualFiles.WriteFile("_layout.md", @"
+The Header
-context.VirtualFiles.WriteFile("page.md", @"
-## {{ title }}
+{{ page }}");
-The Content");
var result = new PageResult(context.GetPage("page"))
+
var result = new PageResult(context.GetPage("page"))
{
- Args = { {"title", "The Title"} },
- ContentType = MimeTypes.Html,
- OutputTransformers = { MarkdownPageFormat.TransformToHtml },
+ Args = { {"title", "The Title"} },
+ ContentType = MimeTypes.Html,
+ OutputTransformers = { MarkdownPageFormat.TransformToHtml },
};
-var html = await result.RenderToStringAsync();
var context = new TemplateContext {
- PageFormats = { new MarkdownPageFormat() }
+
var context = new ScriptContext {
+ PageFormats = { new MarkdownPageFormat() }
}.Init();
-context.VirtualFiles.WriteFile("_layout.html", @"
-<html>
- <title>{{ title }}</title>
-</head>
-<body>
- {{ page }}
-</body>");
+context.VirtualFiles.WriteFile("_layout.html", @"
+<html>
+ <title>{{ title }}</title>
+</head>
+<body>
+ {{ page }}
+</body>");
-context.VirtualFiles.WriteFile("page.md", @"
-## Transformers
-
-The Content");
var result = new PageResult(context.GetPage("page"))
+
var result = new PageResult(context.GetPage("page"))
{
- Args = { {"title", "The Title"} },
- ContentType = MimeTypes.Html,
- PageTransformers = { MarkdownPageFormat.TransformToHtml },
+ Args = { {"title", "The Title"} },
+ ContentType = MimeTypes.Html,
+ PageTransformers = { MarkdownPageFormat.TransformToHtml },
};
-var html = await result.RenderToStringAsync();
var context = new TemplateContext
+
var context = new ScriptContext
{
- TemplateFilters = { new TemplateProtectedFilters() },
- FilterTransformers =
+ ScriptMethods = { new ProtectedScripts() },
+ FilterTransformers =
{
- ["markdown"] = MarkdownPageFormat.TransformToHtml
+ ["markdown"] = MarkdownPageFormat.TransformToHtml
}
}.Init();
-context.VirtualFiles.WriteFile("doc.md", "## The Heading
+context.VirtualFiles.WriteFile("doc.md", "## The Heading\nThe Content");
-The Content");
-
-context.VirtualFiles.WriteFile("page.html", "
-<div id="content">
- {{ 'doc.md' | includeFile | markdown }}
-</div>");
var html = new PageResult(context.GetPage("page")).Result;
var html = new PageResult(context.GetPage("page")).Result;
Plugins.Add(new TemplatePagesFeature { HtmlExtension = "htm" });
public object Any(MyRequest request)
-{
- ...
- return new HttpResult(response)
- {
- View = "CustomPage",
- Template = "_custom-layout",
- };
-}
{{#if id}}
- {{ `select o.Id,
- ${sqlConcat(["e.FirstName", "' '", "e.LastName"])} Employee,
- OrderDate, ShipCountry, ShippedDate,
- ${sqlCurrency("sum((d.Unitprice * d.Quantity) - d.discount)")} Total
- from ${sqlQuote("Order")} o
- inner join
- OrderDetail d on o.Id = d.OrderId
- inner join
- Employee e on o.EmployeeId = e.Id
- where CustomerId = @id
- group by o.Id, EmployeeId, FirstName, LastName, OrderDate, ShipCountry, ShippedDate`
- | dbSelect({ id })
- | assignTo: orders }}
-{{/if}}Plugins.Add(new CustomPlugin { ShowProcessLinks = true });
-Plugins.Add(new OpenApiFeature());
-Plugins.Add(new PostmanFeature());
-Plugins.Add(new CorsFeature());
-Plugins.Add(new ValidationFeature { ScanAppHostAssemblies = true });
public class CustomPlugin : IPlugin
-{
- public bool ShowDrivesLinks { get; set; } = true;
-
- public bool ShowProcessLinks { get; set; }
-
- public void Register(IAppHost appHost)
- {
- if (ShowDrivesLinks)
- {
- var diskFormat = Env.IsWindows ? "NTFS" : "ext2";
- appHost.GetPlugin<MetadataFeature>()
- .AddPluginLink("/drives", "All Disks")
- .AddPluginLink($"/drives?DriveFormatIn={diskFormat}", $"{diskFormat} Disks");
- }
-
- if (ShowProcessLinks)
- {
- appHost.GetPlugin<MetadataFeature>()
- .AddPluginLink("/processes", "All Processes")
- .AddPluginLink("/process/current", "Current Process");
- }
- }
-}
<html>
-<head>
-<title>{{ title ?? 'Redis Vue WebApp' }}</title>
-<i hidden>{{ '/js/hot-loader.js' | ifDebugIncludeScript }}</i>
-
-...
-<link rel="stylesheet" href="../assets/css/bootstrap.css">
-<link rel="stylesheet" href="../assets/css/default.css">
-</head>
-<body>
- <h2 id="title"><a href="/"><img src="/assets/img/redis-logo.png" /> {{ title }}</a></h2>
-
- {{ page }}
-
- <script src="../assets/js/html-utils.js"></script>
- <script src="../assets/js/vue{{ '.min' | if(!debug) }}.js"></script>
- <script src="../assets/js/axios.min.js"></script>
-
- {{ scripts | raw }}
-</body>
-</html>
+
+
+Script
+
Language
Ecosystem
+
+Dynamic
+
Encapsulated
+
Simple
+
Surrounding Ecosystem
+Popular Use Cases
Web Apps
+SharpApps
+
+Pure Cloud Apps
Learn ServiceStack Templates
+Learn SharpScript
Learn ServiceStack Templates
{{ "linq-preview" | partial({ rows: 7, example: "linq01" }) }}
-Free for OSS and Commercial Projects
+Free!
- LinqContext = new TemplateContext {
+ LinqContext = new ScriptContext {
Args = {
- [TemplateConstants.DefaultDateFormat] = "yyyy/MM/dd",
+ [ScriptConstants.DefaultDateFormat] = "yyyy/MM/dd",
["products"] = TemplateQueryData.Products,
["customers"] = TemplateQueryData.Customers,
["comparer"] = new CaseInsensitiveComparer(),
diff --git a/src/wwwroot/usecases/adhoc-querying.html b/src/wwwroot/usecases/adhoc-querying.html
index 6a3bb9f..d048d2e 100644
--- a/src/wwwroot/usecases/adhoc-querying.html
+++ b/src/wwwroot/usecases/adhoc-querying.html
@@ -4,10 +4,10 @@
-->
-Register DB Filters
+Register DB Scripts
- If using ServiceStack's TemplatePagesFeature it's Container has already been reassigned to use
- ServiceStack's Funq IOC so it only needs to be registered once in ServiceStack's IOC.
+ If using ServiceStack's SharpPagesFeature it's Container has already been reassigned to use
+ ServiceStack's Funq IOC so it only needs to be registered once in ServiceStack's IOC.
Always protect free text APIs
Populating the database
Website Sub Directory
@@ -19,7 +19,7 @@ Website Sub Directory
A lightweight solution that can be embedded in any existing ASP.NET or ASP.NET Core Web Application is to embed
and maintain an entire Website in a stand-alone sub directory. To showcase an example we've ported the content in
the .NET Core Razor Rockstars into the
- /usecases/rockstar-files
+ /usecases/rockstar-files
sub directory with the entire website viewable from:
@@ -47,9 +47,8 @@ Porting an existing Razor Website
-->Porting an existing Razor Website
@@ -79,7 +78,7 @@
@@ -106,14 +105,14 @@ Porting an existing Razor Website
/Views/Shared/DeadLayout.cshtml
- /dead/_layout.html
+ /dead/_layout.html
@@ -88,7 +87,7 @@
Porting an existing Razor Website
/Views/Shared/MenuAlive.cshtml
- /menu-alive.html
+ /menu-alive.html
@@ -97,7 +96,7 @@
Porting an existing Razor Website
/alive/vedder/default.cshtml
- /alive/vedder/index.html
+ /alive/vedder/index.html
Embedding Remote Content
{{ pass: url | includeUrlWithCache | markdown }}Implementation
This example uses this simple Service below to generate the HTML and plain-text email previews of this email template:
-EmailTemplatesService.cs
+EmailTemplatesService.cs
{{ 'gfm/email-templates/01.md' | githubMarkdown }}
default.js
+ default.js
to make an ajax request on every text box change.
diff --git a/src/wwwroot/usecases/index.html b/src/wwwroot/usecases/index.html
index 6d62521..e96f769 100644
--- a/src/wwwroot/usecases/index.html
+++ b/src/wwwroot/usecases/index.html
@@ -4,11 +4,11 @@
-->
Web Apps
+Sharp Apps
Email Templates
Live Documents
Introspect State
Adhoc Querying
diff --git a/src/wwwroot/usecases/introspect-state.html b/src/wwwroot/usecases/introspect-state.html
index 73980a9..4f139f5 100644
--- a/src/wwwroot/usecases/introspect-state.html
+++ b/src/wwwroot/usecases/introspect-state.html
@@ -4,7 +4,7 @@
-->
Implementation
IntrospectStateServices.cs
+IntrospectStateServices.cs
{{ 'gfm/introspect-state/01.md' | githubMarkdown }}
@@ -63,7 +63,7 @@ Client UI
Client UI
Debug Template
+Debug Inspector
includeUrl
page: 'page',
files:
{
- 'page.html' : '{{ "https://raw.githubusercontent.com/NetCoreApps/TemplatePages/master/src" | assignTo: src }}
+ 'page.html' : '{{ "https://raw.githubusercontent.com/ServiceStack/sharpscript/master/src" | assignTo: src }}
{{ `${src}/wwwroot/code/linq01.txt` | includeUrl }}'
}
})
diff --git a/src/wwwroot/gfm/sharp-apps/14.html b/src/wwwroot/gfm/sharp-apps/14.html
index fa91bed..a7db5a5 100644
--- a/src/wwwroot/gfm/sharp-apps/14.html
+++ b/src/wwwroot/gfm/sharp-apps/14.html
@@ -1,4 +1,4 @@
-
diff --git a/src/wwwroot/gfm/sharp-apps/14.md b/src/wwwroot/gfm/sharp-apps/14.md
index 6053d4a..001fd54 100644
--- a/src/wwwroot/gfm/sharp-apps/14.md
+++ b/src/wwwroot/gfm/sharp-apps/14.md
@@ -1,4 +1,4 @@
-[](http://blog.web-app.io)
+[](http://blog.web-app.io)
> Live Demo: [blog.web-app.io](http://blog.web-app.io)
diff --git a/src/wwwroot/usecases/rockstar-files/alive/grohl-url/index.html b/src/wwwroot/usecases/rockstar-files/alive/grohl-url/index.html
index 224cdde..1103193 100644
--- a/src/wwwroot/usecases/rockstar-files/alive/grohl-url/index.html
+++ b/src/wwwroot/usecases/rockstar-files/alive/grohl-url/index.html
@@ -31,7 +31,7 @@
- {{ "https://raw.githubusercontent.com/NetCoreApps/TemplatePages/master/src/wwwroot/usecases/rockstar-files/alive/grohl/content.md"
+ {{ "https://raw.githubusercontent.com/ServiceStack/sharpscript/master/src/wwwroot/usecases/rockstar-files/alive/grohl/content.md"
| includeUrlWithCache | markdown }}
Redis Vue
Simple Vue App
Server Pages
Server Pages
Server Handling
Northwind
form to filter results, multi-nested
detail pages and
deep-linking for quickly navigating between
- referenced data. SharpScript is also a great solution for rapidly developing Web APIs where the
+ referenced data. #Script is also a great solution for rapidly developing Web APIs where the
/api/customers.html
API Page below:
@@ -683,8 +683,8 @@ app.azure.settings
Multi-RDBMS SQL
Develop back-end using .NET IDE's
includes removing MVC and Razor dependencies and configuration, extracting its
_layout.html and
converting index.html
- to use SharpScript from its original
+ to use #Script from its original
default.cshtml.
It's also been enhanced with the ability to evaluate scripts from the Chat window, as seen in the screenshot above.
diff --git a/src/wwwroot/docs/sharp-pages.html b/src/wwwroot/docs/sharp-pages.html
index 9922ac6..bc07425 100644
--- a/src/wwwroot/docs/sharp-pages.html
+++ b/src/wwwroot/docs/sharp-pages.html
@@ -4,7 +4,7 @@
-->
Sharp Pages in ServiceStack
Runs Everywhere
@@ -345,7 +345,7 @@
Init Pages
Ignoring Pages
Shorthand arrow expression syntax
Special string argument syntax
response: post,
format: 'json',
contentType: 'application/json',
- 'X-Powered-By': 'SharpScript',
+ 'X-Powered-By': '#Script',
})
httpResult above behaves similarly to customizing a HTTP response using return arguments:httpResult filter is useful for returning a custom HTTP Response without a Response Body, e.g. the New Post page
uses httpFilter to
redirect back to the Users posts page
diff --git a/src/wwwroot/gfm/sharp-apis/04.md b/src/wwwroot/gfm/sharp-apis/04.md
index b0488ad..3753e17 100644
--- a/src/wwwroot/gfm/sharp-apis/04.md
+++ b/src/wwwroot/gfm/sharp-apis/04.md
@@ -59,7 +59,7 @@ The [/preview.html](https://github.com/NetCoreWebApps/Blog/blob/master/app/previ
```
The preview API above is what provides the new [Blog Web App's](http://blog.web-app.io/) Live Preview feature where it will render any
-SharpScript provided in the **content** Query String or HTTP Post Form Data, e.g:
+#Script provided in the **content** Query String or HTTP Post Form Data, e.g:
- [/preview?content={{10|times|select:{pow(index,2)},}}](http://blog.web-app.io/preview?content={{10|times|select:{pow(index,2)},}})
@@ -126,7 +126,7 @@ httpResult({
response: post,
format: 'json',
contentType: 'application/json',
- 'X-Powered-By': 'SharpScript',
+ 'X-Powered-By': '#Script',
})
```
@@ -135,7 +135,7 @@ Any other unknown arguments like **'X-Powered-By'** are returned as HTTP Respons
Returning the `httpResult` above behaves similarly to customizing a HTTP response using return arguments:
```hbs
-{{ post | return({ format:'json', 'X-Powered-By':'SharpScript' }) }}
+{{ post | return({ format:'json', 'X-Powered-By':'#Script' }) }}
```
Using the explicit `httpResult` filter is useful for returning a custom HTTP Response without a Response Body, e.g. the **New Post** page
diff --git a/src/wwwroot/gfm/sharp-apps/14.html b/src/wwwroot/gfm/sharp-apps/14.html
index a7db5a5..48c3584 100644
--- a/src/wwwroot/gfm/sharp-apps/14.html
+++ b/src/wwwroot/gfm/sharp-apps/14.html
@@ -42,7 +42,7 @@
the database instead of
localStorage.
Server Validation
-Pretty URLs by default
/
- /index.html
+ /index.html
@@ -179,17 +179,17 @@ /index.html
- /index.html
+ /index.html
Dynamic Page Routes
/ServiceStack
- /_user/index.html
+ /_user/index.html
user=ServiceStack
/posts/markdown-example
- /posts/_slug/index.html
+ /posts/_slug/index.html
slug=markdown-example
@@ -335,7 +335,7 @@ /posts/markdown-example/edit
- /posts/_slug/edit.html
+ /posts/_slug/edit.html
slug=markdown-example
Init Pages
- This is used in the Blog Web App's _init.html + This is used in the Blog Web App's _init.html where it will create a new blog.sqlite database if it doesn't exist seeded with the UserInfo and Posts Tables and initial data, e.g:
diff --git a/src/wwwroot/gfm/sharp-apis/04.html b/src/wwwroot/gfm/sharp-apis/04.html index 744e43e..b741ca8 100644 --- a/src/wwwroot/gfm/sharp-apis/04.html +++ b/src/wwwroot/gfm/sharp-apis/04.html @@ -17,7 +17,7 @@Usage: /hello/{name}
An API which returns the same wire response as above can be implemented in API Pages by creating a page at -/hello/_name/index.html +/hello/_name/index.html that includes the 1-liner:
Which supports the same content negotiation as a ServiceStack Service where calling it in a browser will generate a @@ -41,7 +41,7 @@
-Usage: /preview?content={templates}
The /preview.html API page uses this to force a plain-text response with:
+The /preview.html API page uses this to force a plain-text response with:
The preview API above is what provides the new Blog Web App's Live Preview feature where it will render any diff --git a/src/wwwroot/gfm/sharp-apis/04.md b/src/wwwroot/gfm/sharp-apis/04.md index 3753e17..a65e5ff 100644 --- a/src/wwwroot/gfm/sharp-apis/04.md +++ b/src/wwwroot/gfm/sharp-apis/04.md @@ -19,7 +19,7 @@ public class HelloService : Service > Usage: /hello/{name} An API which returns the same wire response as above can be implemented in API Pages by creating a page at -[/hello/_name/index.html](https://github.com/NetCoreWebApps/Blog/blob/master/app/hello/_name/index.html) +[/hello/_name/index.html](https://github.com/NetCoreWebApps/Blog/blob/master/hello/_name/index.html) that includes the 1-liner: ```hbs @@ -51,7 +51,7 @@ Templates and Dynamic API Pages to implement all of its functionality. > Usage: /preview?content={templates} -The [/preview.html](https://github.com/NetCoreWebApps/Blog/blob/master/app/preview.html) API page uses this to force a plain-text response with: +The [/preview.html](https://github.com/NetCoreWebApps/blog/blob/master/preview.html) API page uses this to force a plain-text response with: ```hbs {{ content | evalTemplate({use:{plugins:'MarkdownScriptPlugin'}}) | assignTo:response }} diff --git a/src/wwwroot/gfm/sharp-apps/14.html b/src/wwwroot/gfm/sharp-apps/14.html index 48c3584..78b9547 100644 --- a/src/wwwroot/gfm/sharp-apps/14.html +++ b/src/wwwroot/gfm/sharp-apps/14.html @@ -42,7 +42,7 @@
localStorage.
The new.html and edit.html pages shows examples of performing server validation with #Script:
+The new.html and edit.html pages shows examples of performing server validation with #Script:
The output of the _init page is captured in the initout argument which can be later inspected as a normal template argument as seen in
-log.html:
<div>
Output from init.html:
diff --git a/src/wwwroot/gfm/sharp-pages/10.md b/src/wwwroot/gfm/sharp-pages/10.md
index 19938d6..cc1b94a 100644
--- a/src/wwwroot/gfm/sharp-pages/10.md
+++ b/src/wwwroot/gfm/sharp-pages/10.md
@@ -27,7 +27,7 @@
```
The output of the `_init` page is captured in the `initout` argument which can be later inspected as a normal template argument as seen in
-[log.html](https://github.com/NetCoreWebApps/Blog/blob/master/app/log.html):
+[log.html](https://github.com/NetCoreWebApps/Blog/blob/master/log.html):
```html
From 2b39b788ab45cd74ca8422c47bd144b0c1859bd0 Mon Sep 17 00:00:00 2001
From: Demis Bellot
Date: Thu, 21 Mar 2019 05:12:08 -0400
Subject: [PATCH 013/206] Update TemplateServices.cs
---
src/TemplateServices.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/TemplateServices.cs b/src/TemplateServices.cs
index 2b14fac..00b4b63 100644
--- a/src/TemplateServices.cs
+++ b/src/TemplateServices.cs
@@ -79,7 +79,7 @@ public async Task Any(EvaluateScript request)
context.Init();
var pageResult = new PageResult(context.OneTimePage(request.Template))
{
- Args = base.Request.GetTemplateRequestParams(importRequestParams:true)
+ Args = base.Request.GetScriptRequestParams(importRequestParams:true)
};
return await pageResult.RenderToStringAsync(); // render to string so [ReturnExceptionsInJson] can detect Exceptions and return JSON
}
From 0bf2584427af6f8d9756764f7e5b319620472a49 Mon Sep 17 00:00:00 2001
From: Demis Bellot
Date: Fri, 29 Mar 2019 01:42:06 -0400
Subject: [PATCH 014/206] Update Sharp Pages docs
---
src/wwwroot/docs/sharp-pages.html | 76 +++++++++++++++++++++++------
src/wwwroot/gfm/sharp-pages/11.html | 6 +--
src/wwwroot/gfm/sharp-pages/11.md | 6 +--
src/wwwroot/gfm/sharp-pages/12.html | 8 +++
src/wwwroot/gfm/sharp-pages/12.md | 9 ++++
src/wwwroot/gfm/sharp-pages/13.html | 8 +++
src/wwwroot/gfm/sharp-pages/13.md | 9 ++++
7 files changed, 101 insertions(+), 21 deletions(-)
create mode 100644 src/wwwroot/gfm/sharp-pages/12.html
create mode 100644 src/wwwroot/gfm/sharp-pages/12.md
create mode 100644 src/wwwroot/gfm/sharp-pages/13.html
create mode 100644 src/wwwroot/gfm/sharp-pages/13.md
diff --git a/src/wwwroot/docs/sharp-pages.html b/src/wwwroot/docs/sharp-pages.html
index b6b015e..6f9c53c 100644
--- a/src/wwwroot/docs/sharp-pages.html
+++ b/src/wwwroot/docs/sharp-pages.html
@@ -92,12 +92,20 @@ Runs Everywhere
static pages can come alive with access to Default Scripts.
+Content Pages
+
+
+ There are a number of different ways you can use #Script to render dynamic pages, requests that calls and renders
+ #Script .html pages directly are called "Content Pages" which don't use any Services or Controllers
+ and can be called using a number of different styles and calling conventions:
+
+
Page Based Routing
Any .html page available from your AppHost's configured Virtual File Sources
- can be called directly, typically this would mean the File System which in a .NET Core Web App starts from the WebRootPath
- (usually /wwwroot) so a request to /docs/sharp-pages goes through all configured VirtualFileSources to find the first
+ can be called directly, typically this would mean the File System which in a .NET Core Web App starts from the WebRootPath
+ (e.g /wwwroot) so a request to /docs/sharp-pages goes through all configured VirtualFileSources to find the first
match, which for this website is the file
/src/wwwroot/docs/sharp-pages.html.
@@ -223,26 +231,64 @@ Layout and partial recommended naming conventions
{{/raw}}
-View Pages
+View Pages
+
+
+ "View Pages" are pages that are rendered after a Service is executed, where it's typically used to provide the "HTML UI"
+ for the Service's Response DTO where it's populated in the Model page argument
+ as done in Razor ViewPages.
+
- View Pages lets you use .html Sharp Pages to render the HTML for Services Responses.
- It works similarly to Razor ViewPages
- where it uses first matching View Page with the Response DTO is injected as the `Model` property.
- The View Pages can be in any folder within the /Views folder using the format {PageName}.html where PageName
- can be either the Request DTO or Response DTO Name, but all page names within the /Views folder need to be unique.
+ View Pages can be in any nested folder within the /Views folder but all page names within the /Views folder need to be unique.
+ The name should use the format using the format {PageName}.html where PageName can be either:
+
+ - Request DTO Name (e.g. GetContact)
+ - Response DTO Name (e.g. GetContactResponse)
+
+
+
+ There are a number of other ways to specify which View you want to render:
+
+
+Specify the Services DefaultView
+
- Just like ServiceStack.Razor, you can specify to use different Views or Layouts by returning a custom HttpResult, e.g:
+ You can specify which view all Services should use to render their responses by using the [DefaultView] Request Filter Attribute:
+
+
+{{ 'gfm/sharp-pages/12.md' | githubMarkdown }}
+
+Specify View with custom HttpResult
+
+
+ Just like ServiceStack.Razor, you can specify to use different Views or Layouts by returning a
+ decorated response in custom HttpResult with the View or Template you want the Service rendered in , e.g:
{{ 'gfm/sharp-pages/09.md' | githubMarkdown }}
+Return Custom PageResult
+
- Or add the [ClientCanSwapTemplates] Request Filter attribute to allow clients to specify which View and Template to use via the query
- string, e.g: ?View=CustomPage&Template=_custom-layout
+ For maximum flexibility to control how views are rendered you can return a custom PageResult using
+ Request.GetPage() or Request.GetCodePage() extension methods as seen in
+ Model View Controller:
+
+{{ 'gfm/model-view-controller/02.md' | githubMarkdown }}
+
+Allow Views to be specified on the QueryString
+
+
+ You can use the [ClientCanSwapTemplates] Request Filter attribute to let the View and Template by specified on the QueryString,
+ e.g: ?View=CustomPage&Template=_custom-layout
+
+
+{{ 'gfm/sharp-pages/13.md' | githubMarkdown }}
+
Additional examples of dynamically specifying the View and Template are available in
SharpViewsTests.cs.
@@ -365,7 +411,7 @@
Admin Service
users and can be called with:
-/templates/admin
+/script/admin
Which will display the available actions which are currently only:
@@ -387,14 +433,14 @@
Zero downtime deployments
Actions can be invoked in the format with:
-/templates/admin/{Actions}
+/script/admin/{Actions}
Which can be used to call 1 or more actions:
-/templates/admin/invalidateAllCaches
-/templates/admin/invalidateAllCaches,RunInitPage
+/script/admin/invalidateAllCaches
+/script/admin/invalidateAllCaches,RunInitPage
By default it's only available to be called by **Admin** Users (or AuthSecret)
diff --git a/src/wwwroot/gfm/sharp-pages/11.html b/src/wwwroot/gfm/sharp-pages/11.html
index 222f2a3..a600f80 100644
--- a/src/wwwroot/gfm/sharp-pages/11.html
+++ b/src/wwwroot/gfm/sharp-pages/11.html
@@ -1,7 +1,7 @@
Plugins.Add(new SharpPagesFeature {
- TemplatesAdminRole = RoleNames.AllowAnyUser, // Allow any Authenticated User to call /templates/admin
- //TemplatesAdminRole = RoleNames.AllowAnon, // Allow anyone
- //TemplatesAdminRole = null, // Do not register /templates/admin service
+ ScriptAdminRole = RoleNames.AllowAnyUser, // Allow any Authenticated User to call /script/admin
+ //ScriptAdminRole = RoleNames.AllowAnon, // Allow anyone
+ //ScriptAdminRole = null, // Do not register /script/admin service
});
This also the preferred way to enable the Metadata Debug Inspector
in production, which is only available in DebugMode and Admin Role by default:
diff --git a/src/wwwroot/gfm/sharp-pages/11.md b/src/wwwroot/gfm/sharp-pages/11.md
index b729a33..2c69456 100644
--- a/src/wwwroot/gfm/sharp-pages/11.md
+++ b/src/wwwroot/gfm/sharp-pages/11.md
@@ -1,8 +1,8 @@
```csharp
Plugins.Add(new SharpPagesFeature {
- TemplatesAdminRole = RoleNames.AllowAnyUser, // Allow any Authenticated User to call /templates/admin
- //TemplatesAdminRole = RoleNames.AllowAnon, // Allow anyone
- //TemplatesAdminRole = null, // Do not register /templates/admin service
+ ScriptAdminRole = RoleNames.AllowAnyUser, // Allow any Authenticated User to call /script/admin
+ //ScriptAdminRole = RoleNames.AllowAnon, // Allow anyone
+ //ScriptAdminRole = null, // Do not register /script/admin service
});
```
diff --git a/src/wwwroot/gfm/sharp-pages/12.html b/src/wwwroot/gfm/sharp-pages/12.html
new file mode 100644
index 0000000..82f3ea3
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-pages/12.html
@@ -0,0 +1,8 @@
+[DefaultView("CustomPage")]
+public class MyServices
+{
+ public object Any(CreateContact request) => ...;
+
+ public object Any(UpdateContact request) => ...;
+}
+
\ No newline at end of file
diff --git a/src/wwwroot/gfm/sharp-pages/12.md b/src/wwwroot/gfm/sharp-pages/12.md
new file mode 100644
index 0000000..82a1b82
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-pages/12.md
@@ -0,0 +1,9 @@
+```csharp
+[DefaultView("CustomPage")]
+public class MyServices
+{
+ public object Any(CreateContact request) => ...;
+
+ public object Any(UpdateContact request) => ...;
+}
+```
diff --git a/src/wwwroot/gfm/sharp-pages/13.html b/src/wwwroot/gfm/sharp-pages/13.html
new file mode 100644
index 0000000..a8b19f5
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-pages/13.html
@@ -0,0 +1,8 @@
+[ClientCanSwapTemplates]
+public class MyServices
+{
+ public object Any(CreateContact request) => ...;
+
+ public object Any(UpdateContact request) => ...;
+}
+
\ No newline at end of file
diff --git a/src/wwwroot/gfm/sharp-pages/13.md b/src/wwwroot/gfm/sharp-pages/13.md
new file mode 100644
index 0000000..3c9079f
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-pages/13.md
@@ -0,0 +1,9 @@
+```csharp
+[ClientCanSwapTemplates]
+public class MyServices
+{
+ public object Any(CreateContact request) => ...;
+
+ public object Any(UpdateContact request) => ...;
+}
+```
From cb5763a172891cdfd3454813a7d8c1d3377d8195 Mon Sep 17 00:00:00 2001
From: Demis Bellot
Date: Tue, 2 Apr 2019 02:31:38 -0400
Subject: [PATCH 015/206] Add Sharp Scripts page + update home page
---
src/wwwroot/assets/css/default.css | 4 +-
src/wwwroot/docs/api-reference.html | 2 +-
src/wwwroot/docs/code-pages.html | 2 +-
src/wwwroot/docs/deploying-sharp-apps.html | 2 +-
src/wwwroot/docs/expression-viewer.html | 2 +-
src/wwwroot/docs/filters-reference.html | 2 +-
src/wwwroot/docs/model-view-controller.html | 2 +-
src/wwwroot/docs/mvc-netcore.html | 2 +-
src/wwwroot/docs/sandbox.html | 2 +-
src/wwwroot/docs/sharp-apps.html | 2 +-
src/wwwroot/docs/sharp-scripts.html | 143 +++++++++++++++++
src/wwwroot/gfm/sharp-scripts/01.html | 42 +++++
src/wwwroot/gfm/sharp-scripts/01.md | 43 ++++++
src/wwwroot/gfm/sharp-scripts/02.html | 39 +++++
src/wwwroot/gfm/sharp-scripts/02.md | 41 +++++
src/wwwroot/gfm/sharp-scripts/03.html | 20 +++
src/wwwroot/gfm/sharp-scripts/03.md | 21 +++
src/wwwroot/gfm/sharp-scripts/04.html | 20 +++
src/wwwroot/gfm/sharp-scripts/04.md | 21 +++
src/wwwroot/gfm/sharp-scripts/05.html | 29 ++++
src/wwwroot/gfm/sharp-scripts/05.md | 30 ++++
src/wwwroot/gfm/sharp-scripts/06.html | 14 ++
src/wwwroot/gfm/sharp-scripts/06.md | 14 ++
src/wwwroot/gfm/sharp-scripts/07.html | 30 ++++
src/wwwroot/gfm/sharp-scripts/07.md | 30 ++++
src/wwwroot/gfm/sharp-scripts/08.html | 104 +++++++++++++
src/wwwroot/gfm/sharp-scripts/08.md | 28 ++++
src/wwwroot/gfm/sharp-scripts/09.html | 46 ++++++
src/wwwroot/gfm/sharp-scripts/09.md | 47 ++++++
src/wwwroot/index.html | 161 ++++++++++++--------
30 files changed, 868 insertions(+), 77 deletions(-)
create mode 100644 src/wwwroot/docs/sharp-scripts.html
create mode 100644 src/wwwroot/gfm/sharp-scripts/01.html
create mode 100644 src/wwwroot/gfm/sharp-scripts/01.md
create mode 100644 src/wwwroot/gfm/sharp-scripts/02.html
create mode 100644 src/wwwroot/gfm/sharp-scripts/02.md
create mode 100644 src/wwwroot/gfm/sharp-scripts/03.html
create mode 100644 src/wwwroot/gfm/sharp-scripts/03.md
create mode 100644 src/wwwroot/gfm/sharp-scripts/04.html
create mode 100644 src/wwwroot/gfm/sharp-scripts/04.md
create mode 100644 src/wwwroot/gfm/sharp-scripts/05.html
create mode 100644 src/wwwroot/gfm/sharp-scripts/05.md
create mode 100644 src/wwwroot/gfm/sharp-scripts/06.html
create mode 100644 src/wwwroot/gfm/sharp-scripts/06.md
create mode 100644 src/wwwroot/gfm/sharp-scripts/07.html
create mode 100644 src/wwwroot/gfm/sharp-scripts/07.md
create mode 100644 src/wwwroot/gfm/sharp-scripts/08.html
create mode 100644 src/wwwroot/gfm/sharp-scripts/08.md
create mode 100644 src/wwwroot/gfm/sharp-scripts/09.html
create mode 100644 src/wwwroot/gfm/sharp-scripts/09.md
diff --git a/src/wwwroot/assets/css/default.css b/src/wwwroot/assets/css/default.css
index a03f77d..640d0bf 100644
--- a/src/wwwroot/assets/css/default.css
+++ b/src/wwwroot/assets/css/default.css
@@ -232,7 +232,9 @@ caption {
}
::-webkit-scrollbar { width:7px; height:5px }
-::-webkit-scrollbar-thumb { background-color:#ccc; }
+::-webkit-scrollbar-thumb {
+ background-color: rgba(204, 204, 204, .2);
+}
@media (max-width: 1366px) {
#content {
diff --git a/src/wwwroot/docs/api-reference.html b/src/wwwroot/docs/api-reference.html
index 5e72cdb..1355b61 100644
--- a/src/wwwroot/docs/api-reference.html
+++ b/src/wwwroot/docs/api-reference.html
@@ -1,6 +1,6 @@
Plugins
diff --git a/src/wwwroot/docs/code-pages.html b/src/wwwroot/docs/code-pages.html
index 998af57..0826403 100644
--- a/src/wwwroot/docs/code-pages.html
+++ b/src/wwwroot/docs/code-pages.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/deploying-sharp-apps.html b/src/wwwroot/docs/deploying-sharp-apps.html
index 911208e..5ba70cf 100644
--- a/src/wwwroot/docs/deploying-sharp-apps.html
+++ b/src/wwwroot/docs/deploying-sharp-apps.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/expression-viewer.html b/src/wwwroot/docs/expression-viewer.html
index e2c5638..98f6186 100644
--- a/src/wwwroot/docs/expression-viewer.html
+++ b/src/wwwroot/docs/expression-viewer.html
@@ -1,6 +1,6 @@
{{#raw}}
diff --git a/src/wwwroot/docs/filters-reference.html b/src/wwwroot/docs/filters-reference.html
index 474c98e..87df2f8 100644
--- a/src/wwwroot/docs/filters-reference.html
+++ b/src/wwwroot/docs/filters-reference.html
@@ -1,6 +1,6 @@
{{ 'nameContains,tab' | importRequestParams }}
diff --git a/src/wwwroot/docs/model-view-controller.html b/src/wwwroot/docs/model-view-controller.html
index 78372d6..067119c 100644
--- a/src/wwwroot/docs/model-view-controller.html
+++ b/src/wwwroot/docs/model-view-controller.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/mvc-netcore.html b/src/wwwroot/docs/mvc-netcore.html
index 075c2dd..321fa78 100644
--- a/src/wwwroot/docs/mvc-netcore.html
+++ b/src/wwwroot/docs/mvc-netcore.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/sandbox.html b/src/wwwroot/docs/sandbox.html
index bce9d5e..b1d1202 100644
--- a/src/wwwroot/docs/sandbox.html
+++ b/src/wwwroot/docs/sandbox.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/sharp-apps.html b/src/wwwroot/docs/sharp-apps.html
index 24d8428..ed8ae14 100644
--- a/src/wwwroot/docs/sharp-apps.html
+++ b/src/wwwroot/docs/sharp-apps.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/sharp-scripts.html b/src/wwwroot/docs/sharp-scripts.html
new file mode 100644
index 0000000..2ffd396
--- /dev/null
+++ b/src/wwwroot/docs/sharp-scripts.html
@@ -0,0 +1,143 @@
+
+
+{{#markdown}}
+The [web .NET Core tool](https://docs.servicestack.net/web-new) also serve as a `#Script` runner. The Vue and React "lite" project templates
+take advantage of this in their [Pre-compiled minified production _bundle.ss script](https://docs.servicestack.net/templates-lite#pre-compiled-minified-production-bundles)
+which is run with `web run {script}`:
+
+ $ dotnet tool install --global web
+
+ $ web run wwwroot/_bundle.ss
+
+**[Sharp Scripts](/docs/sharp-scripts)** are **run in the same context** and have access to the same functionality and features as a
+[Sharp App](/docs/sharp-apps) including extensibility va [custom plugins](/docs/sharp-apps#plugins).
+They can run **stand-alone** independent of an
+[app.settings](/docs/sharp-apps#ideal-for-web-designers-and-content-authors) config file, instead the app settings configuration
+can be added in its page arguments to enable or configure any features.
+
+Lets go through a couple of different possibilities we can do with scripts:
+
+### Adhoc reports
+
+Scripts can use the built-in [Database Scripts](/docs/db-scripts) to be able to [run queries against any](/docs/sharp-apps#multi-platform-configurations) `sqlite`, `sqlserver`, `mysql` and `postgres` database and quickly view data snapshots using the built-in
+[HTML Scripts](/docs/html-scripts#htmldump), e.g:
+{{/markdown}}
+
+{{ 'gfm/sharp-scripts/01.md' | githubMarkdown }}
+
+{{#markdown}}
+#### Specifying Script Arguments
+
+The above script generates a static HTML page can be invoked with **any number of named arguments** after the script name, in this case it
+generates a report for Northwind Order **#10643**, saves it to `10643.html` and opens it in the OS's default browser:
+
+ $ web run script.html -id 10643 > 10643.html && start 10643.html
+
+Which looks like:
+
+
+
+### textDump
+
+Generating static `.html` pages can quickly produce reports that looks good enough to share with others,
+but if you just want to see a snapshot info at a glance or be able to share in text-based mediums like email or chat
+channels you can replace `htmlDump` with `textDump` where it will instead output GitHub flavored Markdown tables, e.g:
+{{/markdown}}
+
+{{ 'gfm/sharp-scripts/02.md' | githubMarkdown }}
+
+{{#markdown}}
+As the output is human-readable we can view directly it without a browser:
+
+ $ web run script.ss -id 10643
+
+Which will output:
+{{/markdown}}
+
+{{ 'gfm/sharp-scripts/07.md' | githubMarkdown }}
+
+{{#markdown}}
+And because they're GitHub Flavored Markdown Tables they can be embedded directly in Markdown docs (like this) where it's renders as:
+{{/markdown}}
+
+
+{{ 'gfm/sharp-scripts/08.md' | githubMarkdown }}
+
+
+{{#markdown}}
+### AWS Dashboards
+
+The [comprehensive built-in scripts](/docs/default-scripts) coupled with ServiceStack's agnostic
+providers like the [Virtual File System](/virtual-file-system) makes it easy to quickly query infrastructure resources
+like all Tables and Row counts in managed AWS RDS Instances or Search for static Asset resources in S3 Buckets.
+{{/markdown}}
+
+{{ 'gfm/sharp-scripts/03.md' | githubMarkdown }}
+
+{{#markdown}}
+You can use `$NAME` to move confidential information out of public scripts where it will be replaced with Environment
+Variables. Then run the script as normal and optionally override the `find` pattern for files you want to search for:
+
+ $ web run script-aws.ss -find *.png
+
+Where it displays a dashboard of activity from your AWS resources: containing all Tables with their Row Counts,
+adhoc queries like your last 5 Orders, The Root files and Folders available in your S3 Bucket and any matching resources
+from your `find` search pattern:
+{{/markdown}}
+
+{{ 'gfm/sharp-scripts/09.md' | githubMarkdown }}
+
+{{#markdown}}
+### Azure Dashboards
+
+The nice thing about `#Script` late-binding and cloud agnostic providers is that with just different configuration we
+can **use the exact same script** to query an Azure managed SQL Server Database and Azure Blob File Storage:
+{{/markdown}}
+
+{{ 'gfm/sharp-scripts/04.md' | githubMarkdown }}
+
+{{#markdown}}
+## Live #Script with 'web watch'
+
+What's even nicer than the fast feedback of running adhoc scripts? Is the instant feedback you get from being able to **"watch"** the same script!
+
+To watch a script just replace `run` with `watch`:
+
+ $ web watch script-aws.ss -find *.png
+
+[](/docs/sharp-scripts)
+
+The ability to run stand-alone adhoc scripts in an extensible dynamic scripting language feels like you're
+using a "developer enhanced" SQL Studio, where you can combine queries from multiple data sources, [manipulate them with LINQ](https://sharpscript.net/linq/restriction-operators)
+and quickly pipe results to dump utils to combine them in the same output for instant visualization.
+
+`#Script` scripts can also be easily shared, maintained in gists and run on all different Win/OSX/Linux OS's that .NET Core runs on.
+
+### Live Transformations
+
+Another area where "watched" scripts can shine is as a "companion scratch pad" assistant during development that you can quickly switch to
+and instantly test out live code fragments, calculations and transformations, e.g. This ends up being a great way to test out markdown syntax
+and Nuglify's advanced compression using our new `minifyjs` and `minifycss` [Script Blocks](/docs/blocks):
+{{/markdown}}
+
+{{ 'gfm/sharp-scripts/05.md' | githubMarkdown }}
+
+{{#markdown}}
+Then run with:
+
+ $ web watch livepad.ss
+
+Which starts a live watched session that re-renders itself on save, initially with:
+{{/markdown}}
+
+{{ 'gfm/sharp-scripts/06.md' | githubMarkdown }}
+
+{{#markdown}}
+### Live Session
+
+[](/docs/sharp-scripts)
+{{/markdown}}
+
diff --git a/src/wwwroot/gfm/sharp-scripts/01.html b/src/wwwroot/gfm/sharp-scripts/01.html
new file mode 100644
index 0000000..bc29ed4
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/01.html
@@ -0,0 +1,42 @@
+
+
\ No newline at end of file
diff --git a/src/wwwroot/gfm/sharp-scripts/01.md b/src/wwwroot/gfm/sharp-scripts/01.md
new file mode 100644
index 0000000..378e3f0
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/01.md
@@ -0,0 +1,43 @@
+```hbs
+
+
+
+
+Order Report #{{id}}
+
+{{ `SELECT o.Id, OrderDate, CustomerId, Freight, e.Id as EmployeeId, s.CompanyName as ShipVia,
+ ShipAddress, ShipCity, ShipPostalCode, ShipCountry
+ FROM "Order" o
+ INNER JOIN
+ Employee e ON o.EmployeeId = e.Id
+ INNER JOIN
+ Shipper s ON o.ShipVia = s.Id
+ WHERE o.Id = @id`
+ | dbSingle({ id }) | assignTo: order }}
+
+{{#with order}}
+ {{ "table table-striped" | assignTo: className }}
+
+
+
+ {{ order | htmlDump({ caption: 'Order Details', className }) }}
+ {{ `SELECT * FROM Customer WHERE Id = @CustomerId`
+ | dbSingle({ CustomerId }) | htmlDump({ caption: `Customer Details`, className }) }}
+ {{ `SELECT Id, LastName, FirstName, Title, City, Country, Extension FROM Employee WHERE Id=@EmployeeId`
+ | dbSingle({ EmployeeId }) | htmlDump({ caption: `Employee Details`, className }) }}
+
+
+ {{ `SELECT p.ProductName, ${sqlCurrency("od.UnitPrice")} UnitPrice, Quantity, Discount
+ FROM OrderDetail od
+ INNER JOIN
+ Product p ON od.ProductId = p.Id
+ WHERE OrderId = @id`
+ | dbSelect({ id })
+ | htmlDump({ caption: "Line Items", className }) }}
+{{else}}
+ {{ `There is no Order with id: ${id}` }}
+{{/with}}
+```
diff --git a/src/wwwroot/gfm/sharp-scripts/02.html b/src/wwwroot/gfm/sharp-scripts/02.html
new file mode 100644
index 0000000..6ea8439
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/02.html
@@ -0,0 +1,39 @@
+
+
\ No newline at end of file
diff --git a/src/wwwroot/gfm/sharp-scripts/02.md b/src/wwwroot/gfm/sharp-scripts/02.md
new file mode 100644
index 0000000..32649eb
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/02.md
@@ -0,0 +1,41 @@
+
+```hbs
+
+
+{{ `SELECT o.Id, OrderDate, CustomerId, Freight, e.Id as EmployeeId, s.CompanyName as ShipVia,
+ ShipAddress, ShipCity, ShipPostalCode, ShipCountry
+ FROM "Order" o
+ INNER JOIN
+ Employee e ON o.EmployeeId = e.Id
+ INNER JOIN
+ Shipper s ON o.ShipVia = s.Id
+ WHERE o.Id = @id`
+ | dbSingle({ id }) | assignTo: order }}
+
+{{#with order}}
+
+{{ order | textDump({ caption: 'Order Details' }) }}
+
+{{ `SELECT p.ProductName, ${sqlCurrency("od.UnitPrice")} UnitPrice, Quantity, Discount
+ FROM OrderDetail od
+ INNER JOIN
+ Product p ON od.ProductId = p.Id
+ WHERE OrderId = @id`
+ | dbSelect({ id })
+ | textDump({ caption: "Line Items" })
+}}
+{{ `SELECT ${sqlCurrency("(od.UnitPrice * Quantity)")} AS OrderTotals
+ FROM OrderDetail od
+ INNER JOIN
+ Product p ON od.ProductId = p.Id
+ WHERE OrderId = @id
+ ORDER BY 1 DESC`
+ | dbSelect({ id })
+ | textDump({ rowNumbers: false }) }}
+{{else}}
+ {{ `There is no Order with id: ${id}` }}
+{{/with}}
+```
\ No newline at end of file
diff --git a/src/wwwroot/gfm/sharp-scripts/03.html b/src/wwwroot/gfm/sharp-scripts/03.html
new file mode 100644
index 0000000..0c47e67
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/03.html
@@ -0,0 +1,20 @@
+
+
\ No newline at end of file
diff --git a/src/wwwroot/gfm/sharp-scripts/03.md b/src/wwwroot/gfm/sharp-scripts/03.md
new file mode 100644
index 0000000..90dafd0
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/03.md
@@ -0,0 +1,21 @@
+```hbs
+
+
+{{ dbTableNamesWithRowCounts | textDump({ caption: 'Tables' }) }}
+
+{{ `SELECT "Id", "CustomerId", "EmployeeId", "OrderDate" from "Order" ORDER BY "Id" DESC ${sqlLimit(5)}`
+ | dbSelect | textDump({ caption: 'Last 5 Orders', headerStyle:'None' }) }}
+
+{{ contentAllRootDirectories | map => `${it.Name}/`
+ | union(map(contentAllRootFiles, x => x.Name))
+ | textDump({ caption: 'Root Files and Folders' }) }}
+
+{{ find ?? '*.html' | assignTo: find }}
+{{ find | contentFilesFind | map => it.VirtualPath | take(15)
+ | textDump({ caption: `Files matching: ${find}` }) }}
+```
\ No newline at end of file
diff --git a/src/wwwroot/gfm/sharp-scripts/04.html b/src/wwwroot/gfm/sharp-scripts/04.html
new file mode 100644
index 0000000..3612996
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/04.html
@@ -0,0 +1,20 @@
+
+
\ No newline at end of file
diff --git a/src/wwwroot/gfm/sharp-scripts/04.md b/src/wwwroot/gfm/sharp-scripts/04.md
new file mode 100644
index 0000000..6efaf74
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/04.md
@@ -0,0 +1,21 @@
+```hbs
+
+
+{{ dbTableNamesWithRowCounts | textDump({ caption: 'Tables' }) }}
+
+{{ `SELECT "Id", "CustomerId", "EmployeeId", "OrderDate" from "Order" ORDER BY "Id" DESC ${sqlLimit(5)}`
+ | dbSelect | textDump({ caption: 'Last 5 Orders', headerStyle:'None' }) }}
+
+{{ contentAllRootDirectories | map => `${it.Name}/`
+ | union(map(contentAllRootFiles, x => x.Name))
+ | textDump({ caption: 'Root Files and Folders' }) }}
+
+{{ find ?? '*.html' | assignTo: find }}
+{{ find | contentFilesFind | map => it.VirtualPath | take(5)
+ | textDump({ caption: `Files matching: ${find}` }) }}
+```
\ No newline at end of file
diff --git a/src/wwwroot/gfm/sharp-scripts/05.html b/src/wwwroot/gfm/sharp-scripts/05.html
new file mode 100644
index 0000000..a4e7114
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/05.html
@@ -0,0 +1,29 @@
+
+
\ No newline at end of file
diff --git a/src/wwwroot/gfm/sharp-scripts/05.md b/src/wwwroot/gfm/sharp-scripts/05.md
new file mode 100644
index 0000000..7ee5962
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/05.md
@@ -0,0 +1,30 @@
+```hbs
+
+
+Markdown:
+{{#markdown}}
+## Title
+
+> quote
+
+Paragraph with [a link](https://example.org).
+{{/markdown}}
+
+JS:
+{{#minifyjs}}
+function add(left, right) {
+ return left + right;
+}
+add(1, 2);
+{{/minifyjs}}
+
+
+CSS:
+{{#minifycss}}
+body {
+ background-color: #ffffff;
+}
+{{/minifycss}}
+```
diff --git a/src/wwwroot/gfm/sharp-scripts/06.html b/src/wwwroot/gfm/sharp-scripts/06.html
new file mode 100644
index 0000000..f506033
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/06.html
@@ -0,0 +1,14 @@
+Markdown:
+<h2 id="title">Title</h2>
+<blockquote>
+<p>quote</p>
+</blockquote>
+<p>Paragraph with <a href="https://example.org">a link</a>.</p>
+
+JS:
+function add(n,t){return n+t}add(1,2)
+
+CSS:
+body{background-color:#fff}
+
+
\ No newline at end of file
diff --git a/src/wwwroot/gfm/sharp-scripts/06.md b/src/wwwroot/gfm/sharp-scripts/06.md
new file mode 100644
index 0000000..080d8a1
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/06.md
@@ -0,0 +1,14 @@
+```
+Markdown:
+Title
+
+quote
+
+Paragraph with a link.
+
+JS:
+function add(n,t){return n+t}add(1,2)
+
+CSS:
+body{background-color:#fff}
+```
diff --git a/src/wwwroot/gfm/sharp-scripts/07.html b/src/wwwroot/gfm/sharp-scripts/07.html
new file mode 100644
index 0000000..95bfb95
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/07.html
@@ -0,0 +1,30 @@
+| Order Details ||
+|------------------|----------------|
+| Id | 10643 |
+| Order Date | 1997-08-25 |
+| Customer Id | ALFKI |
+| Freight | 29.46 |
+| Employee Id | 6 |
+| Ship Via | Speedy Express |
+| Ship Address | Obere Str. 57 |
+| Ship City | Berlin |
+| Ship Postal Code | 12209 |
+| Ship Country | Germany |
+
+
+Line Items
+
+| # | Product Name | Unit Price | Quantity | Discount |
+|---|-------------------|------------|----------|----------|
+| 1 | Rössle Sauerkraut | $45.60 | 15 | 0.25 |
+| 2 | Chartreuse verte | $18.00 | 21 | 0.25 |
+| 3 | Spegesild | $12.00 | 2 | 0.25 |
+
+
+| Order Totals |
+|--------------|
+| $684.00 |
+| $378.00 |
+| $24.00 |
+
+
\ No newline at end of file
diff --git a/src/wwwroot/gfm/sharp-scripts/07.md b/src/wwwroot/gfm/sharp-scripts/07.md
new file mode 100644
index 0000000..775c39e
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/07.md
@@ -0,0 +1,30 @@
+```
+| Order Details ||
+|------------------|----------------|
+| Id | 10643 |
+| Order Date | 1997-08-25 |
+| Customer Id | ALFKI |
+| Freight | 29.46 |
+| Employee Id | 6 |
+| Ship Via | Speedy Express |
+| Ship Address | Obere Str. 57 |
+| Ship City | Berlin |
+| Ship Postal Code | 12209 |
+| Ship Country | Germany |
+
+
+Line Items
+
+| # | Product Name | Unit Price | Quantity | Discount |
+|---|-------------------|------------|----------|----------|
+| 1 | Rössle Sauerkraut | $45.60 | 15 | 0.25 |
+| 2 | Chartreuse verte | $18.00 | 21 | 0.25 |
+| 3 | Spegesild | $12.00 | 2 | 0.25 |
+
+
+| Order Totals |
+|--------------|
+| $684.00 |
+| $378.00 |
+| $24.00 |
+```
\ No newline at end of file
diff --git a/src/wwwroot/gfm/sharp-scripts/08.html b/src/wwwroot/gfm/sharp-scripts/08.html
new file mode 100644
index 0000000..8b60ea0
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/08.html
@@ -0,0 +1,104 @@
+
+
+
+Order Details
+
+
+
+
+
+Id
+10643
+
+
+Order Date
+1997-08-25
+
+
+Customer Id
+ALFKI
+
+
+Freight
+29.46
+
+
+Employee Id
+6
+
+
+Ship Via
+Speedy Express
+
+
+Ship Address
+Obere Str. 57
+
+
+Ship City
+Berlin
+
+
+Ship Postal Code
+12209
+
+
+Ship Country
+Germany
+
+
+
+Line Items
+
+
+
+#
+Product Name
+Unit Price
+Quantity
+Discount
+
+
+
+
+1
+Rössle Sauerkraut
+$45.60
+15
+0.25
+
+
+2
+Chartreuse verte
+$18.00
+21
+0.25
+
+
+3
+Spegesild
+$12.00
+2
+0.25
+
+
+
+
+
+
+Order Totals
+
+
+
+
+$684.00
+
+
+$378.00
+
+
+$24.00
+
+
+
+
\ No newline at end of file
diff --git a/src/wwwroot/gfm/sharp-scripts/08.md b/src/wwwroot/gfm/sharp-scripts/08.md
new file mode 100644
index 0000000..36ffdd0
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/08.md
@@ -0,0 +1,28 @@
+| Order Details ||
+|------------------|----------------|
+| Id | 10643 |
+| Order Date | 1997-08-25 |
+| Customer Id | ALFKI |
+| Freight | 29.46 |
+| Employee Id | 6 |
+| Ship Via | Speedy Express |
+| Ship Address | Obere Str. 57 |
+| Ship City | Berlin |
+| Ship Postal Code | 12209 |
+| Ship Country | Germany |
+
+
+Line Items
+
+| # | Product Name | Unit Price | Quantity | Discount |
+|---|-------------------|------------|----------|----------|
+| 1 | Rössle Sauerkraut | $45.60 | 15 | 0.25 |
+| 2 | Chartreuse verte | $18.00 | 21 | 0.25 |
+| 3 | Spegesild | $12.00 | 2 | 0.25 |
+
+
+| Order Totals |
+|--------------|
+| $684.00 |
+| $378.00 |
+| $24.00 |
diff --git a/src/wwwroot/gfm/sharp-scripts/09.html b/src/wwwroot/gfm/sharp-scripts/09.html
new file mode 100644
index 0000000..abf7c97
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/09.html
@@ -0,0 +1,46 @@
+| Tables ||
+|--------------------|------|
+| Order Detail | 2155 |
+| Order | 830 |
+| Customer | 91 |
+| Product | 77 |
+| Territory | 53 |
+| Region | 0 |
+| Shipper | 0 |
+| Supplier | 0 |
+| Category | 0 |
+| Employee | 0 |
+| Employee Territory | 0 |
+
+
+Last 5 Orders
+
+| # | Id | CustomerId | EmployeeId | OrderDate |
+|---|-------|------------|------------|------------|
+| 1 | 11077 | RATTC | 1 | 1998-05-06 |
+| 2 | 11076 | BONAP | 4 | 1998-05-06 |
+| 3 | 11075 | RICSU | 8 | 1998-05-06 |
+| 4 | 11074 | SIMOB | 7 | 1998-05-06 |
+| 5 | 11073 | PERIC | 2 | 1998-05-05 |
+
+
+| Root Files and Folders |
+|------------------------|
+| api/ |
+| northwind/ |
+| rockstars/ |
+| index.html |
+| web.aws.settings |
+| web.postgres.settings |
+| web.sqlite.settings |
+| web.sqlserver.settings |
+
+
+| Files matching: *.png |
+|-----------------------------------------|
+| assets/img/logo-32.png |
+| rockstars/img/green_dust_scratch.png |
+| rockstars/img/rip_jobs.png |
+| rockstars/img/tileable_wood_texture.png |
+
+
\ No newline at end of file
diff --git a/src/wwwroot/gfm/sharp-scripts/09.md b/src/wwwroot/gfm/sharp-scripts/09.md
new file mode 100644
index 0000000..e65660d
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/09.md
@@ -0,0 +1,47 @@
+```
+| Tables ||
+|--------------------|------|
+| Order Detail | 2155 |
+| Order | 830 |
+| Customer | 91 |
+| Product | 77 |
+| Territory | 53 |
+| Region | 0 |
+| Shipper | 0 |
+| Supplier | 0 |
+| Category | 0 |
+| Employee | 0 |
+| Employee Territory | 0 |
+
+
+Last 5 Orders
+
+| # | Id | CustomerId | EmployeeId | OrderDate |
+|---|-------|------------|------------|------------|
+| 1 | 11077 | RATTC | 1 | 1998-05-06 |
+| 2 | 11076 | BONAP | 4 | 1998-05-06 |
+| 3 | 11075 | RICSU | 8 | 1998-05-06 |
+| 4 | 11074 | SIMOB | 7 | 1998-05-06 |
+| 5 | 11073 | PERIC | 2 | 1998-05-05 |
+
+
+| Root Files and Folders |
+|------------------------|
+| api/ |
+| northwind/ |
+| rockstars/ |
+| index.html |
+| web.aws.settings |
+| web.postgres.settings |
+| web.sqlite.settings |
+| web.sqlserver.settings |
+
+
+| Files matching: *.png |
+|-----------------------------------------|
+| assets/img/logo-32.png |
+| rockstars/img/green_dust_scratch.png |
+| rockstars/img/rip_jobs.png |
+| rockstars/img/tileable_wood_texture.png |
+```
+
diff --git a/src/wwwroot/index.html b/src/wwwroot/index.html
index 2a62b92..19bed87 100644
--- a/src/wwwroot/index.html
+++ b/src/wwwroot/index.html
@@ -12,91 +12,53 @@
within a sandbox environment - ensuring scripts are encapsulated and developed using clean, reusable and testable components.
-Language
-
-
- The language itself a familiar combination of
- JavaScript expressions contained within Template Expressions popularized in
- Vue.js filters and
- Angular's Template Expressions
- whilst statements adopt Handlebars block helpers syntax. The language is also highly extensible which is pre-configured
- comprehensive suite of safe methods and blocks by default, but are all registered via its
- rich plugin functionality which can be completely removed or "shadowed" (aka overridden) where it could be used to form the basis of your
- own completely different DSL.
-
-
-Ecosystem
+Popular Use Cases
- In addition to the language, #Script includes a surrounding ecosystem of rich functionality adapting #Script
- to power a number of fun and exciting scenarios. E.g. Scripts are lazily loaded from its Virtual File System
- which can be configured to use any of the available Virtual File Systems
- and is what enables Pure Cloud Apps where entire Sharp Apps could be hosted within
- your AWS S3 Bucket or Azure Blob Storage.
+ These qualities opens #Script up to a number of new use-cases that's better suited than Razor for maintaining
+ content-heavy websites, live documents,
+ Email Templates and can easily
+ introspect the state of running .NET Apps where they provide
+ valuable insight at a glance with support for
+ Adhoc querying.
-Dynamic
-
-
- It's small, lightweight footprint and built-in Hot Reloading
- provides a fun, clean and productive alternative to MVC Razor that's easily
- integrated into any web framework and runs identically in
- every platform ServiceStack runs on, as well as
- within ASP.NET MVC and ASP.NET MVC Core
- Controllers - in all cases, using the same high-performance implementation
- to asynchronously write to a forward-only OutputStream for max performance and maximum potential reuse of your Source Code.
-
+Sharp Scripts
- #Script scripts are lazily loaded and late-bound for Instant Startup, doesn't require any
- pre-compilation, coupling to any external configuration files, build tools, designer tooling or have any special deployment requirements.
- It can be used as a general purpose scripting or templating language to generate any text format
- and includes built-in support for .html.
+ The easiest way to get started is to run a Sharp Script in
+ watch mode
+ by creating a text file with the .ss extension and running it with web watch <name>.ss.
+ Scripts have access to all built-in Scripts and ServiceStack's which makes it easy to create scripts
+ that run against
+ all supported RDBMS's
+ and cloud agnostic ServiceStack.Aws and
+ ServiceStack.Azure as seen below with the same script
+ below performs adhoc queries against an AWS RDS PostgreSQL managed database and S3 File Storage and an Azure SQL Server
+ Database and Azure Blob File Storage:
-Encapsulated
-
-
- Similar to Vue.js Components, Sharp Script's are evaluated in an
- Encapsulated Sandbox that enables fine-grained control over exactly what functionality and instances
- are available to different Scripts. They're pre-configured with a comprehensive suite of safe
- Default Scripts which when running in trusted contexts can easily be granted access to
- enhanced functionality.
-
+
-Simple
+Live Transformations
- #Script is designed to be incrementally adoptable where its initial form is
- ideal for non-programmers,
- that can gradually adopt more power and functionality when needed where they can leverage existing Services or MVC Controllers
- to enable an
- MVC programming model or have .html pages upgraded to use
- Code Pages where they can utilize the full unlimited power of the C# programming language to
- enable precise control over the rendering of pages and partials. Code pages take precedence and are interchangeable wherever
- normal .html pages are requested making them a non-invasive alternative whenever advanced functionality is required.
+ The watch mode enables an live REPL which serves as an interactive explanatory playground
+ that's especially useful in scripts where you want instant feedback like viewing the results of
+ live transformations:
-Popular Use Cases
-
-
- These qualities opens #Script up to a number of new use-cases that's better suited than Razor for maintaining
- content-heavy websites, live documents,
- Email Templates and can easily
- introspect the state of running .NET Apps where they provide
- valuable insight at a glance with support for
- Adhoc querying.
-
+
Sharp Apps
- One use-case made possible by #Script we're extremely excited about is Sharp Apps - a new approach to
+ One use-case made possible by #Script we're extremely excited about is Sharp Apps - a new approach to
dramatically simplify .NET Web App development and provide the most productive development experience possible.
- Sharp Apps leverages #Script to develop entire content-rich, data-driven websites in a pure live development model
+ Sharp Apps leverages #Script to develop entire content-rich, data-driven websites in a pure live development model
without needing to write any C#, compile projects or manually refresh pages. Checkout the Spirals Sharp App below
for a glimpse of the Live Development experience available:
@@ -137,9 +99,9 @@ Sharp Apps
Pure Cloud Apps
- Sharp Apps also enable the development of Pure Cloud Apps where the same SharpApp
- can be developed and run entirely on AWS S3 and RDS or Azure Blob Storage and SQL Server by just changing
- its app.settings.
+ Sharp Apps also enable the development of Pure Cloud Apps
+ where the same SharpApp can be developed and run entirely on AWS S3 and RDS or Azure Blob Storage and SQL Server
+ by just changing its app.settings.
Learn #Script
@@ -161,6 +123,71 @@ Learn #Script
{{ "linq-preview" | partial({ rows: 7, example: "linq01" }) }}
+Language
+
+
+ The language itself a familiar combination of
+ JavaScript expressions contained within Template Expressions popularized in
+ Vue.js filters and
+ Angular's Template Expressions
+ whilst statements adopt Handlebars block helpers syntax. The language is also highly extensible which is pre-configured
+ comprehensive suite of safe methods and blocks by default, but are all registered via its
+ rich plugin functionality which can be completely removed or "shadowed" (aka overridden) where it could be used to form the basis of your
+ own completely different DSL.
+
+
+Ecosystem
+
+
+ In addition to the language, #Script includes a surrounding ecosystem of rich functionality adapting #Script
+ to power a number of fun and exciting scenarios. E.g. Scripts are lazily loaded from its Virtual File System
+ which can be configured to use any of the available Virtual File Systems
+ and is what enables Pure Cloud Apps where entire Sharp Apps could be hosted within
+ your AWS S3 Bucket or Azure Blob Storage.
+
+
+Dynamic
+
+
+ It's small, lightweight footprint and built-in Hot Reloading
+ provides a fun, clean and productive alternative to MVC Razor that's easily
+ integrated into any web framework and runs identically in
+ every platform ServiceStack runs on, as well as
+ within ASP.NET MVC and ASP.NET Core MVC
+ Controllers - in all cases, using the same high-performance implementation
+ to asynchronously write to a forward-only OutputStream for max performance and maximum potential reuse of your Source Code.
+
+
+
+ #Script scripts are lazily loaded and late-bound for Instant Startup, doesn't require any
+ pre-compilation, coupling to any external configuration files, build tools, designer tooling or have any special deployment requirements.
+ It can be used as a general purpose scripting or templating language to generate any text format
+ and includes built-in support for .html.
+
+
+Encapsulated
+
+
+ Similar to Vue.js Components, Sharp Script's are evaluated in an
+ Encapsulated Sandbox that enables fine-grained control over exactly what functionality and instances
+ are available to different Scripts. They're pre-configured with a comprehensive suite of safe
+ Default Scripts which when running in trusted contexts can easily be granted access to
+ enhanced functionality.
+
+
+Simple
+
+
+ #Script is designed to be incrementally adoptable where its initial form is
+ ideal for non-programmers,
+ that can gradually adopt more power and functionality when needed where they can leverage existing Services or MVC Controllers
+ to enable an
+ MVC programming model or have .html pages upgraded to use
+ Code Pages where they can utilize the full unlimited power of the C# programming language to
+ enable precise control over the rendering of pages and partials. Code pages take precedence and are interchangeable wherever
+ normal .html pages are requested making them a non-invasive alternative whenever advanced functionality is required.
+
+
Free!
From 5a7b6954e7ad72c16c1f560d8a4994cf301a4369 Mon Sep 17 00:00:00 2001
From: Demis Bellot
Date: Tue, 2 Apr 2019 09:55:39 -0400
Subject: [PATCH 016/206] use same color as active item
---
src/wwwroot/assets/css/default.css | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/wwwroot/assets/css/default.css b/src/wwwroot/assets/css/default.css
index 640d0bf..a943647 100644
--- a/src/wwwroot/assets/css/default.css
+++ b/src/wwwroot/assets/css/default.css
@@ -233,7 +233,7 @@ caption {
::-webkit-scrollbar { width:7px; height:5px }
::-webkit-scrollbar-thumb {
- background-color: rgba(204, 204, 204, .2);
+ background-color: #f8f8f8;
}
@media (max-width: 1366px) {
From d719790d071ca385dc8cb799ee8e8ba9f98505a3 Mon Sep 17 00:00:00 2001
From: Demis Bellot
Date: Tue, 2 Apr 2019 10:33:52 -0400
Subject: [PATCH 017/206] replace with web new
---
src/wwwroot/docs/installation.html | 18 +++++++++---------
src/wwwroot/docs/sharp-apps.html | 10 +++++-----
2 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/src/wwwroot/docs/installation.html b/src/wwwroot/docs/installation.html
index dcd2367..df651bd 100644
--- a/src/wwwroot/docs/installation.html
+++ b/src/wwwroot/docs/installation.html
@@ -38,27 +38,27 @@ Starter Project Templates
The Starter Projects below provide a quick way to get started with a pre-configured ServiceStack Template Web App.
-.NET Core 2.1 Boostrap Template
+.NET Core Bootstrap Template
Create a new Templates Website .NET Core 2.1 App with
- dotnet-new:
+ web new:
- $ npm install -g @servicestack/cli
+ $ dotnet tool install --global web
- $ dotnet-new templates ProjectName
+ $ web new templates ProjectName
-ASP.NET v4.5 Boostrap Starter
+ASP.NET v4.5 Bootstrap Starter
- For ASP.NET v4.5 projects create a new ServiceStack ASP.NET Templates with Bootstrap from the VS.NET Templates in
+ For ASP.NET v4.5+ projects create a new ServiceStack ASP.NET Templates with Bootstrap from the VS.NET Templates in
ServiceStackVS VS.NET Extension
to create an ASP.NET v4.5 Project using
ServiceStack's recommended project structure:
@@ -82,12 +82,12 @@
SharpApp Project Templates
Bare SharpApp
- To start with a simple and mininal website, create a new bare-webapp project template:
+ To start with a simple and minimal website, create a new bare-webapp project template:
-$ dotnet-new bare-webapp ProjectName
+$ web new bare-webapp ProjectName
This creates a multi-page Bootstrap Website with Menu navigation that's ideal for content-heavy Websites.
@@ -102,7 +102,7 @@
Parcel SharpApp
-$ dotnet-new parcel-webapp ProjectName
+$ web new parcel-webapp ProjectName
This provides a simple and powerful starting template for developing modern JavaScript .NET Core Sharp Apps utilizing the
diff --git a/src/wwwroot/docs/sharp-apps.html b/src/wwwroot/docs/sharp-apps.html
index ed8ae14..ad180b4 100644
--- a/src/wwwroot/docs/sharp-apps.html
+++ b/src/wwwroot/docs/sharp-apps.html
@@ -149,20 +149,20 @@
SharpApp Project Templates
A more complete starting option is to start from one of the project templates below. All project templates can be installed using the
- dotnet-new tool, which if not already can be installed with:
+ web new tool, which if not already can be installed with:
-$ npm install -g @servicestack/cli
+$ dotnet tool install --global web
Bare SharpApp
- To start with a simple and mininal website, create a new bare-webapp project template:
+ To start with a simple and minimal website, create a new bare-webapp project template:
-$ dotnet-new bare-webapp ProjectName
+$ web new bare-webapp ProjectName
This creates a multi-page Bootstrap Website with Menu navigation that's ideal for content-heavy Websites.
@@ -177,7 +177,7 @@
Parcel SharpApp
-$ dotnet-new parcel-webapp ProjectName
+$ web new parcel-webapp ProjectName
This provides a simple and powerful starting template for developing modern JavaScript .NET Core Sharp Apps utilizing the
From abb771a9b2a178c6b68148a27e415b2977dcfbf5 Mon Sep 17 00:00:00 2001
From: Demis Bellot
Date: Tue, 2 Apr 2019 13:28:20 -0400
Subject: [PATCH 018/206] add production bundles
---
src/wwwroot/docs/sharp-scripts.html | 13 +++++++++++++
src/wwwroot/gfm/sharp-scripts/10.html | 24 ++++++++++++++++++++++++
src/wwwroot/gfm/sharp-scripts/10.md | 25 +++++++++++++++++++++++++
src/wwwroot/gfm/sharp-scripts/11.html | 6 ++++++
src/wwwroot/gfm/sharp-scripts/11.md | 7 +++++++
5 files changed, 75 insertions(+)
create mode 100644 src/wwwroot/gfm/sharp-scripts/10.html
create mode 100644 src/wwwroot/gfm/sharp-scripts/10.md
create mode 100644 src/wwwroot/gfm/sharp-scripts/11.html
create mode 100644 src/wwwroot/gfm/sharp-scripts/11.md
diff --git a/src/wwwroot/docs/sharp-scripts.html b/src/wwwroot/docs/sharp-scripts.html
index 2ffd396..d7c1bd5 100644
--- a/src/wwwroot/docs/sharp-scripts.html
+++ b/src/wwwroot/docs/sharp-scripts.html
@@ -7,11 +7,24 @@
The [web .NET Core tool](https://docs.servicestack.net/web-new) also serve as a `#Script` runner. The Vue and React "lite" project templates
take advantage of this in their [Pre-compiled minified production _bundle.ss script](https://docs.servicestack.net/templates-lite#pre-compiled-minified-production-bundles)
which is run with `web run {script}`:
+{{/markdown}}
+
+{{ 'gfm/sharp-scripts/10.md' | githubMarkdown }}
+
+{{#markdown}}
+Which can be run with the `web` tool:
$ dotnet tool install --global web
$ web run wwwroot/_bundle.ss
+Which will create the production bundles, minify all already non-minified bundles and write them to disk with the paths written visible in the
+`#Script` **_bundle.ss** output:
+{{/markdown}}
+
+{{ 'gfm/sharp-scripts/11.md' | githubMarkdown }}
+
+{{#markdown}}
**[Sharp Scripts](/docs/sharp-scripts)** are **run in the same context** and have access to the same functionality and features as a
[Sharp App](/docs/sharp-apps) including extensibility va [custom plugins](/docs/sharp-apps#plugins).
They can run **stand-alone** independent of an
diff --git a/src/wwwroot/gfm/sharp-scripts/10.html b/src/wwwroot/gfm/sharp-scripts/10.html
new file mode 100644
index 0000000..d42dfe0
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/10.html
@@ -0,0 +1,24 @@
+
+
\ No newline at end of file
diff --git a/src/wwwroot/gfm/sharp-scripts/10.md b/src/wwwroot/gfm/sharp-scripts/10.md
new file mode 100644
index 0000000..e769fed
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/10.md
@@ -0,0 +1,25 @@
+```hbs
+{{* run in host project directory with `web run wwwroot/_bundle.ss` *}}
+
+{{ false | assignTo: debug }}
+{{ (debug ? '' : '.min') | assignTo: min }}
+{{ [`/css/bundle${min}.css`,`/js/lib.bundle${min}.js`,`/js/bundle${min}.js`] | map => fileDelete(it) | end }}
+
+{{* Copy same bundle definitions from _layout.html as-is *}}
+
+{{ ['/assets/css/'] | bundleCss({ minify:!debug, cache:!debug, disk:!debug, out:`/css/bundle${min}.css` }) }}
+
+{{ [
+ `/lib/vue/dist/vue${min}.js`,
+ `/lib/vue-router/dist/vue-router${min}.js`,
+ '/lib/vue-class-component/vue-class-component.js',
+ '/lib/vue-property-decorator/vue-property-decorator.umd.js',
+ '/lib/@servicestack/client/servicestack-client.umd.js',
+] | bundleJs({ minify:!debug, cache:!debug, disk:!debug, out:`/js/lib.bundle${min}.js` }) }}
+
+{{ [
+ 'content:/src/components/',
+ 'content:/src/shared/',
+ 'content:/src/',
+] | bundleJs({ minify:!debug, cache:!debug, disk:!debug, out:`/js/bundle${min}.js` }) }}
+```
\ No newline at end of file
diff --git a/src/wwwroot/gfm/sharp-scripts/11.html b/src/wwwroot/gfm/sharp-scripts/11.html
new file mode 100644
index 0000000..bf3dc1d
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/11.html
@@ -0,0 +1,6 @@
+<link rel="stylesheet" href="/css/bundle.min.css">
+
+<script src="/js/lib.bundle.min.js"></script>
+
+<script src="/js/bundle.min.js"></script>
+
\ No newline at end of file
diff --git a/src/wwwroot/gfm/sharp-scripts/11.md b/src/wwwroot/gfm/sharp-scripts/11.md
new file mode 100644
index 0000000..d985bcc
--- /dev/null
+++ b/src/wwwroot/gfm/sharp-scripts/11.md
@@ -0,0 +1,7 @@
+```html
+
+
+
+
+
+```
\ No newline at end of file
From 898f9df61583e105591266ba9d4931b4d5303819 Mon Sep 17 00:00:00 2001
From: Demis Bellot
Date: Tue, 2 Apr 2019 16:32:56 -0400
Subject: [PATCH 019/206] Update sharp-scripts.html
---
src/wwwroot/docs/sharp-scripts.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/wwwroot/docs/sharp-scripts.html b/src/wwwroot/docs/sharp-scripts.html
index d7c1bd5..cbff442 100644
--- a/src/wwwroot/docs/sharp-scripts.html
+++ b/src/wwwroot/docs/sharp-scripts.html
@@ -19,7 +19,7 @@
$ web run wwwroot/_bundle.ss
Which will create the production bundles, minify all already non-minified bundles and write them to disk with the paths written visible in the
-`#Script` **_bundle.ss** output:
+`#Script` *_bundle.ss* output:
{{/markdown}}
{{ 'gfm/sharp-scripts/11.md' | githubMarkdown }}
From 9916c9e8bcafb0040972a0bbfbccc95cd0acc1ce Mon Sep 17 00:00:00 2001
From: Demis Bellot
Date: Wed, 3 Apr 2019 18:42:04 -0400
Subject: [PATCH 020/206] Update sharp-apps.html
---
src/wwwroot/docs/sharp-apps.html | 96 ++++++++++++++++----------------
1 file changed, 48 insertions(+), 48 deletions(-)
diff --git a/src/wwwroot/docs/sharp-apps.html b/src/wwwroot/docs/sharp-apps.html
index ad180b4..8c109c5 100644
--- a/src/wwwroot/docs/sharp-apps.html
+++ b/src/wwwroot/docs/sharp-apps.html
@@ -65,7 +65,7 @@ Getting Started
$ web list
- By default this will list all NetCoreWebApps available, ordered by popularity:
+ By default this will list all sharp-apps available, ordered by popularity:
1. redis Redis Admin Viewer developed as Vue Client Single Page App
@@ -73,17 +73,17 @@ Getting Started
3. chat Highly extensible App with custom AppHost leveraging OAuth + SSE for real-time Chat
4. plugins Extend Sharp Apps with Plugins, Filters, ServiceStack Services and other C# extensions
5. blog Minimal multi-user Twitter OAuth blogging platform that creates living powerful pages
-6. rockwind-aws Rockwind Cloud Web App on AWS
-7. rockwind-azure Rockwind Cloud Web App on Azure
+6. rockwind-aws Rockwind Cloud Sharp App on AWS
+7. rockwind-azure Rockwind Cloud Sharp App on Azure
8. redis-html Redis Admin Viewer developed as server-generated HTML Website
9. spirals Explore and generate different Spirals with SVG
-10. rockwind Web App combining multi-layout Rockstars website + data-driven Northwind Browser
+10. rockwind Sharp App combining multi-layout Rockstars website + data-driven Northwind Browser
Usage: web install <name>
Where any of the apps can be installed by specifying the name, e.g.
- spirals can be installed with:
+ spirals can be installed with:
$ web install spirals
@@ -95,7 +95,7 @@ Getting Started
$ cd spirals && web
- Each Web App can also be run as a
+ Each Sharp App can also be run as a
.NET Core Windows Desktop App
by installing the app dotnet tool:
@@ -201,8 +201,8 @@ Cloud Apps Starter Projects
About Bare WebApp
@@ -220,9 +220,9 @@ About Bare WebApp
The benefits over using a static website is improved maintenance
- as you can extract and use its common _layout.html
+ as you can extract and use its common _layout.html
instead of having it duplicated in each page.
- The menu.html partial also makes menu items
+ The menu.html partial also makes menu items
easier to maintain by just adding an entry in the JavaScript object literal. The dynamic menu also takes care of highlighting the active menu item.
@@ -247,7 +247,7 @@ Ideal for Web Designers and Content Authors
with GUI HTML designers.
-app.settings
+app.settings
Below is the app.settings for a Basic App, with contentRoot being the only setting required as the
@@ -280,14 +280,14 @@
Example Sharp Apps
In addition to the templates there's a number of Sharp Apps to illustrate the various features available and to showcase the different
kind of Web Apps that can easily be developed. The source code for each app is available either individually from
- github.com/NetCoreWebApps.
+ github.com/sharp-apps.
Redis HTML
web install redis-html -
redis-html.web-app.io -
- NetCoreWebApps/redis-html
+ sharp-apps/redis-html
@@ -324,10 +324,10 @@
Beautiful, succinct, declarative code
The nice thing about generating HTML is that it's the one true constant in Web development that will always be there.
The entire functionality for the Redis Web App is contained in a single
- /redis-html/app/index.html which includes
+ /redis-html/app/index.html which includes
all Template and JavaScript Source Code in < 200 lines which also includes all as server logic as it doesn't rely on any
back-end Services and just uses the Redis Scripts to interface with Redis directly.
- The source code also serves as a good
+ The source code also serves as a good
demonstration of the declarative coding style that #Script encourages that in addition to being highly-readable requires orders
of magnitude less code than our previous Redis JavaScript SPA's with a comparable feature-set.
@@ -338,7 +338,7 @@ Beautiful, succinct, declarative code
that would be required to convert the existing JavaScript Apps to a use different JavaScript fx.
-app.settings
+app.settings
The app.settings for Redis is similar to Web App Starter above except it adds a redis.connection
@@ -357,7 +357,7 @@
Redis Vue
@@ -372,7 +372,7 @@
Redis Vue
where the UI has been extracted into isolated Vue components utilizing
Vue X-Templates to render the App on the client where
all Redis Vue's functionality is contained within the
- Redis/app/index.html page.
+ Redis/app/index.html page.
@@ -385,8 +385,8 @@ Simple Vue App
dynamic web page when needed.
- The containing _layout.html
- page can be separated from the index.html page
+ The containing _layout.html
+ page can be separated from the index.html page
that contains the App's functionality, where it's able to extract the title of the page and embed it in the
HTML <head/> as well as embed the page's <script /> in its optimal location at the bottom of the
HTML <body/>, after the page's blocking script dependencies:
@@ -402,9 +402,9 @@
Simple Vue App
Where it uses the more verbose and developer-friendly
- vue.js
+ vue.js
during development whilst using the production optimized
- vue.min.js
+ vue.min.js
for deployments. So despite avoiding the complexity tax of an npm-based build system it still gets some of its benefits
like conditional deployments and effortless hot reloading.
@@ -412,7 +412,7 @@ Simple Vue App
Server Pages
- Whilst most of index.html is a static Vue
+ Whilst most of index.html is a static Vue
app, #Script is leveraged to generate the body of the <redis-info/> Component on the initial home page render:
@@ -428,7 +428,7 @@ Server Handling
Another area #Script is used is to handle the HTTP POST where it calls the redisChangeConnection filter to change
the current Redis connection before rendering the
- connection-info.html partial with the
+ connection-info.html partial with the
current connection info:
@@ -441,7 +441,7 @@ Vue Ajax Server APIs
API Pages:
-search.html
+search.html
Called when searching for Redis keys where the query is forwarded to the redisSearchKeys filter:
@@ -449,7 +449,7 @@
call.html
+call.html
Called to execute an arbitrary Redis command on the connected instance, with the response from Redis is returned as a
@@ -476,7 +476,7 @@
Rockwind
@@ -489,13 +489,13 @@
Rockwind
Rockstars
- /rockstars is an
+ /rockstars is an
example of a Content Website that itself maintains multiple sub sections with their own layouts -
- /rockstars/alive
+ /rockstars/alive
for living Rockstars and
- /rockstars/dead
+ /rockstars/dead
for the ones that have died. Each Rockstar maintains their own encapsulated
- mix of HTML, markdown content and splash image
+ mix of HTML, markdown content and splash image
that intuitively uses the closest _layout.html, content.md and splash.jpg from the page they're
referenced from. This approach makes it easy to move entire sub sections over by just moving a folder and it will automatically
use the relevant layout and partials of its parent.
@@ -506,13 +506,13 @@
Rockstars
Northwind
- /northwind is an example of
+ /northwind is an example of
a dynamic UI for a database containing a
form to filter results, multi-nested
detail pages and
deep-linking for quickly navigating between
referenced data. #Script is also a great solution for rapidly developing Web APIs where the
- /api/customers.html
+ /api/customers.html
API Page below:
@@ -688,7 +688,7 @@ Multi-RDBMS SQL
multiple databases by using the
sql* DB Filters to avoid using RDBMS-specific
SQL syntax. The
- /northwind/customer.html
+ /northwind/customer.html
contains a good example containing a number of things to watch out for:
@@ -708,7 +708,7 @@ Rockwind VFS
web install rockwind-aws -
rockwind-aws.web-app.io -
- NetCoreWebApps/rockwind-aws
+ sharp-apps/rockwind-aws
@@ -755,7 +755,7 @@
resolveAsset
Pure Cloud Apps
-rockwind-aws/app.settings
+rockwind-aws/app.settings
The AWS settings shows an example of this where every external resource
@@ -785,18 +785,18 @@
DockerFile
+DockerFile
Deployments are also greatly simplified as all that's needed is to deploy the WebApp binary and app.settings of your Cloud App,
e.g. here's the DockerFile for rockwind-aws.web-app.io - deployed to AWS ECS
- using the deployment scripts in rockwind-aws and following our
+ using the deployment scripts in rockwind-aws and following our
.NET Core Docker Deployment Guideline:
{{ 'gfm/sharp-apps/03.md' | githubMarkdown }}
-rockwind-azure/app.settings
+rockwind-azure/app.settings
We can also create Azure Cloud Apps in the same we've done for AWS above, which runs the same
@@ -822,7 +822,7 @@
Plugins
@@ -855,7 +855,7 @@
Registering ServiceStack Plugins
ServiceStack Plugins can be added to your App by
listing it's Type Name in the features config entry in
- app.settings:
+ app.settings:
debug true
@@ -950,11 +950,11 @@ Chat
- /chat is an example of the ultimate form
+ /chat is an example of the ultimate form
of extensibility where instead of just being able to add Services, Filters and Plugins, etc. You can add your entire
AppHost which Sharp Apps will use instead of its own. This vastly expands the use-cases that can be built with
Sharp Apps as it gives you complete fine-grained control over how your App is configured.
@@ -968,7 +968,7 @@
Develop back-end using .NET IDE's
For chat.web-app.io we've taken a copy of the existing .NET Core 2.1
Chat App and moved its C# code to
/example-plugins/Chat
- and its files to /apps/chat
+ and its files to /apps/chat
where it can be developed like any other Web App except it utilizes the Chat AppHost and implementation in the
SelfHost Chat App.
@@ -977,8 +977,8 @@ Develop back-end using .NET IDE's
Customizations from the original
.NET Core Chat implementation
includes removing MVC and Razor dependencies and configuration, extracting its
- _layout.html and
- converting index.html
+ _layout.html and
+ converting index.html
to use #Script from its original
default.cshtml.
It's also been enhanced with the ability to evaluate scripts from the Chat window, as seen in the screenshot above.
@@ -1021,7 +1021,7 @@ Reusing Web App's app.setting and files
After the back-end has been implemented we can build and copy the compiled Chat.dll into the Chat's
- /plugins folder where
+ /plugins folder where
we can take advantage of the improved development experience for rapidly developing its UI.
@@ -1029,7 +1029,7 @@ Blog
{{ 'gfm/sharp-apps/14.md' | githubMarkdown }}
From d1616fdfd9815380ef2d7b9bd0f9acb403eed76e Mon Sep 17 00:00:00 2001
From: Demis Bellot
Date: Wed, 3 Apr 2019 18:42:50 -0400
Subject: [PATCH 021/206] Update sharp-apps.html
---
src/wwwroot/docs/sharp-apps.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/wwwroot/docs/sharp-apps.html b/src/wwwroot/docs/sharp-apps.html
index 8c109c5..d51fb57 100644
--- a/src/wwwroot/docs/sharp-apps.html
+++ b/src/wwwroot/docs/sharp-apps.html
@@ -65,7 +65,7 @@ Getting Started
$ web list
- By default this will list all sharp-apps available, ordered by popularity:
+ By default this will list all sharp-apps available:
1. redis Redis Admin Viewer developed as Vue Client Single Page App
From 5a2919d521955a1c100d5834224df12e4df27863 Mon Sep 17 00:00:00 2001
From: Demis Bellot
Date: Wed, 3 Apr 2019 18:50:05 -0400
Subject: [PATCH 022/206] highlight #Script
---
src/wwwroot/docs/sharp-apps.html | 30 +++++++++++++++---------------
src/wwwroot/index.html | 10 +++++-----
2 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/src/wwwroot/docs/sharp-apps.html b/src/wwwroot/docs/sharp-apps.html
index d51fb57..5e499e7 100644
--- a/src/wwwroot/docs/sharp-apps.html
+++ b/src/wwwroot/docs/sharp-apps.html
@@ -13,7 +13,7 @@
- Sharp Apps leverages #Script to develop entire content-rich, data-driven websites without needing to write any C#,
+ Sharp Apps leverages #Script to develop entire content-rich, data-driven websites without needing to write any C#,
compile projects or manually refresh pages - resulting in the easiest and fastest way to develop Web Apps in .NET!
@@ -23,8 +23,8 @@ Ultimate Simplicity
Not having to write any C# code or perform any app builds dramatically reduces the cognitive overhead and conceptual knowledge
required for development where the only thing front-end Web developers need to know is #Script's familiar syntax
and what methods are available to call.
- Because of #Script's JavaScript compatibility, developing a Website with #Script will be instantly familiar to JavaScript
- devs despite calling and binding directly to .NET APIs behind the scenes.
+ Because of [#Script's JavaScript compatibility](/docs/expression-viewer), developing a Website with #Script will be instantly familiar to
+ JavaScript devs despite calling and binding directly to .NET APIs behind the scenes.
@@ -232,7 +232,7 @@
Ideal for Web Designers and Content Authors
The other primary benefit is that this is an example of a website that can be maintained by employees who don't have any
- programming experience as #Script in their basic form are intuitive and approachable to non-developers, e.g:
+ programming experience as #Script in their basic form are intuitive and approachable to non-developers, e.g:
The title of each page is maintained as metadata HTML comments:
@@ -242,7 +242,7 @@ Ideal for Web Designers and Content Authors
- #Script's syntax is also the ideal way to convey variable substitution, e.g: <title>{{ pass: title }}</title>
+ #Script syntax is also the ideal way to convey variable substitution, e.g: <title>{{ pass: title }}</title>
and even embedding a partial reads like english {{ pass: 'menu' | partial }} which is both intuitive and works well
with GUI HTML designers.
@@ -293,7 +293,7 @@ Redis HTML
For the Redis Browser Web App, we wanted to implement an App that was an ideal candidate for a Single Page App but constrain ourselves
to do all HTML rendering on the server and have each interaction request a full-page reload to see how a traditional server-generated
- Web App feels like with the performance of .NET Core 2.1 and #Script. We're pleasantly surprised with the result as when
+ Web App feels like with the performance of .NET Core 2.1 and #Script. We're pleasantly surprised with the result as when
the App is run locally the responsiveness is effectively indistinguishable from an Ajax App. When hosted on the Internet
there is a sub-second delay which causes a noticeable flicker but it still retains a pleasant UX that's faster than most websites.
@@ -328,7 +328,7 @@ Beautiful, succinct, declarative code
all Template and JavaScript Source Code in < 200 lines which also includes all as server logic as it doesn't rely on any
back-end Services and just uses the Redis Scripts to interface with Redis directly.
The source code also serves as a good
- demonstration of the declarative coding style that #Script encourages that in addition to being highly-readable requires orders
+ demonstration of the declarative coding style that #Script encourages that in addition to being highly-readable requires orders
of magnitude less code than our previous Redis JavaScript SPA's with a comparable feature-set.
@@ -361,7 +361,7 @@ Redis Vue
- Whilst the above server-generated HTML Redis UI shows how you can easily develop traditional Web Apps using #Script, we've also
+ Whilst the above server-generated HTML Redis UI shows how you can easily develop traditional Web Apps using #Script, we've also
rewritten the Redis UI as a Single Page App which is the more suitable choice for an App like this as it provides a more
optimal and responsive UX by only loading the HTML page once on Startup then utilizes Ajax to only download and update
the incremental parts of the App's UI that needs changing.
@@ -380,7 +380,7 @@
Redis Vue
Simple Vue App
- #Script also provides a great development experience for Single Page Apps which for the most part gets out of your way letting you
+ #Script also provides a great development experience for Single Page Apps which for the most part gets out of your way letting you
develop the Single Page App as if it were a static .html file, but also benefits from the flexibility of a
dynamic web page when needed.
@@ -413,7 +413,7 @@ Server Pages
Whilst most of index.html is a static Vue
- app, #Script is leveraged to generate the body of the <redis-info/> Component on the initial home page render:
+ app, #Script is leveraged to generate the body of the <redis-info/> Component on the initial home page render:
{{ 'gfm/sharp-apps/08.md' | githubMarkdown }}
@@ -426,7 +426,7 @@ Server Pages
Server Handling
- Another area #Script is used is to handle the HTTP POST where it calls the redisChangeConnection filter to change
+ Another area #Script is used is to handle the HTTP POST where it calls the redisChangeConnection filter to change
the current Redis connection before rendering the
connection-info.html partial with the
current connection info:
@@ -511,7 +511,7 @@
Northwind
form to filter results, multi-nested
detail pages and
deep-linking for quickly navigating between
- referenced data. #Script is also a great solution for rapidly developing Web APIs where the
+ referenced data. #Script is also a great solution for rapidly developing Web APIs where the
/api/customers.html
API Page below:
@@ -683,8 +683,8 @@ app.azure.settings
Multi-RDBMS SQL
- As #Script is unable to use a Typed ORM like OrmLite
- to hide the nuances of each database, we need to be a bit more diligent in #Script to use parameterized SQL that works across
+ As #Script is unable to use a Typed ORM like OrmLite
+ to hide the nuances of each database, we need to be a bit more diligent in #Script to use parameterized SQL that works across
multiple databases by using the
sql* DB Filters to avoid using RDBMS-specific
SQL syntax. The
@@ -979,7 +979,7 @@
Develop back-end using .NET IDE's
includes removing MVC and Razor dependencies and configuration, extracting its
_layout.html and
converting index.html
- to use #Script from its original
+ to use #Script from its original
default.cshtml.
It's also been enhanced with the ability to evaluate scripts from the Chat window, as seen in the screenshot above.
diff --git a/src/wwwroot/index.html b/src/wwwroot/index.html
index 19bed87..1877ee3 100644
--- a/src/wwwroot/index.html
+++ b/src/wwwroot/index.html
@@ -15,7 +15,7 @@
Popular Use Cases
- These qualities opens #Script up to a number of new use-cases that's better suited than Razor for maintaining
+ These qualities opens #Script up to a number of new use-cases that's better suited than Razor for maintaining
content-heavy websites, live documents,
Email Templates and can easily
introspect the state of running .NET Apps where they provide
@@ -107,7 +107,7 @@
Pure Cloud Apps
Learn #Script
- Get started learning #Script by going through the Interactive Guide and creating
+ Get started learning #Script by going through the Interactive Guide and creating
a Starter Project.
@@ -139,7 +139,7 @@ Language
Ecosystem
- In addition to the language, #Script includes a surrounding ecosystem of rich functionality adapting #Script
+ In addition to the language, #Script includes a surrounding ecosystem of rich functionality adapting #Script
to power a number of fun and exciting scenarios. E.g. Scripts are lazily loaded from its Virtual File System
which can be configured to use any of the available Virtual File Systems
and is what enables Pure Cloud Apps where entire Sharp Apps could be hosted within
@@ -191,8 +191,8 @@
Simple
Free!
- We believe we've only scratched the surface of what's possible with #Script and we'd love to see what new
- use-cases it can help achieve and help encourage an ecosystem of pluggable and reusable scripts, #Script is
+ We believe we've only scratched the surface of what's possible with #Script and we'd love to see what new
+ use-cases it can help achieve and help encourage an ecosystem of pluggable and reusable scripts, #Script is
being developed in ServiceStack.Common which is a
free library for commercial or non-commercial use.
From 31cc5b9312b6f1336fcf28dbce22576b6dc365c7 Mon Sep 17 00:00:00 2001
From: Demis Bellot
Date: Thu, 4 Apr 2019 19:09:04 -0400
Subject: [PATCH 023/206] Update default.css
---
src/wwwroot/assets/css/default.css | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/wwwroot/assets/css/default.css b/src/wwwroot/assets/css/default.css
index a943647..8df63ab 100644
--- a/src/wwwroot/assets/css/default.css
+++ b/src/wwwroot/assets/css/default.css
@@ -231,8 +231,13 @@ caption {
display: none;
}
-::-webkit-scrollbar { width:7px; height:5px }
-::-webkit-scrollbar-thumb {
+#sidebar::-webkit-scrollbar
+{
+ width:10px;
+ height:15px;
+}
+#sidebar::-webkit-scrollbar-thumb
+{
background-color: #f8f8f8;
}
From d4d5b892e38573ab30e98a78ba68847f6f995c61 Mon Sep 17 00:00:00 2001
From: Demis Bellot
Date: Fri, 5 Apr 2019 18:22:44 -0400
Subject: [PATCH 024/206] Add plugins
---
src/wwwroot/docs/api-reference.html | 2 +-
src/wwwroot/docs/code-pages.html | 2 +-
src/wwwroot/docs/db-scripts.html | 2 +-
src/wwwroot/docs/default-scripts.html | 2 +-
src/wwwroot/docs/deploying-sharp-apps.html | 2 +-
src/wwwroot/docs/error-handling.html | 2 +-
src/wwwroot/docs/expression-viewer.html | 2 +-
src/wwwroot/docs/filters-reference.html | 2 +-
src/wwwroot/docs/hot-reloading.html | 2 +-
src/wwwroot/docs/html-scripts.html | 2 +-
src/wwwroot/docs/introduction.html | 68 +++++-
src/wwwroot/docs/model-view-controller.html | 2 +-
src/wwwroot/docs/mvc-netcore.html | 2 +-
src/wwwroot/docs/partials.html | 2 +-
src/wwwroot/docs/protected-scripts.html | 2 +-
src/wwwroot/docs/redis-scripts.html | 2 +-
src/wwwroot/docs/sandbox.html | 2 +-
src/wwwroot/docs/script-plugins.html | 7 +
src/wwwroot/docs/servicestack-scripts.html | 2 +-
src/wwwroot/docs/sharp-apis.html | 2 +-
src/wwwroot/docs/sharp-apps.html | 2 +-
src/wwwroot/docs/sharp-pages.html | 2 +-
src/wwwroot/docs/sharp-scripts.html | 2 +-
src/wwwroot/docs/transformers.html | 2 +-
src/wwwroot/gfm/introduction/11.html | 12 ++
src/wwwroot/gfm/introduction/11.md | 13 ++
src/wwwroot/gfm/methods/01.html | 4 +-
src/wwwroot/gfm/methods/01.md | 4 +-
src/wwwroot/gfm/methods/09.md | 4 +-
src/wwwroot/gfm/script-plugins/01.html | 179 ++++++++++++++++
src/wwwroot/gfm/script-plugins/01.md | 221 ++++++++++++++++++++
31 files changed, 530 insertions(+), 26 deletions(-)
create mode 100644 src/wwwroot/docs/script-plugins.html
create mode 100644 src/wwwroot/gfm/introduction/11.html
create mode 100644 src/wwwroot/gfm/introduction/11.md
create mode 100644 src/wwwroot/gfm/script-plugins/01.html
create mode 100644 src/wwwroot/gfm/script-plugins/01.md
diff --git a/src/wwwroot/docs/api-reference.html b/src/wwwroot/docs/api-reference.html
index 1355b61..b066045 100644
--- a/src/wwwroot/docs/api-reference.html
+++ b/src/wwwroot/docs/api-reference.html
@@ -1,6 +1,6 @@
Plugins
diff --git a/src/wwwroot/docs/code-pages.html b/src/wwwroot/docs/code-pages.html
index 0826403..22ba25e 100644
--- a/src/wwwroot/docs/code-pages.html
+++ b/src/wwwroot/docs/code-pages.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/db-scripts.html b/src/wwwroot/docs/db-scripts.html
index d510ed1..3944a12 100644
--- a/src/wwwroot/docs/db-scripts.html
+++ b/src/wwwroot/docs/db-scripts.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/default-scripts.html b/src/wwwroot/docs/default-scripts.html
index 49d4835..d32a339 100644
--- a/src/wwwroot/docs/default-scripts.html
+++ b/src/wwwroot/docs/default-scripts.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/deploying-sharp-apps.html b/src/wwwroot/docs/deploying-sharp-apps.html
index 5ba70cf..bd137a2 100644
--- a/src/wwwroot/docs/deploying-sharp-apps.html
+++ b/src/wwwroot/docs/deploying-sharp-apps.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/error-handling.html b/src/wwwroot/docs/error-handling.html
index 83d4d54..98a4b84 100644
--- a/src/wwwroot/docs/error-handling.html
+++ b/src/wwwroot/docs/error-handling.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/expression-viewer.html b/src/wwwroot/docs/expression-viewer.html
index 98f6186..f3bbc1e 100644
--- a/src/wwwroot/docs/expression-viewer.html
+++ b/src/wwwroot/docs/expression-viewer.html
@@ -1,6 +1,6 @@
{{#raw}}
diff --git a/src/wwwroot/docs/filters-reference.html b/src/wwwroot/docs/filters-reference.html
index 87df2f8..c79fe96 100644
--- a/src/wwwroot/docs/filters-reference.html
+++ b/src/wwwroot/docs/filters-reference.html
@@ -1,6 +1,6 @@
{{ 'nameContains,tab' | importRequestParams }}
diff --git a/src/wwwroot/docs/hot-reloading.html b/src/wwwroot/docs/hot-reloading.html
index 9b55442..5317b37 100644
--- a/src/wwwroot/docs/hot-reloading.html
+++ b/src/wwwroot/docs/hot-reloading.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/html-scripts.html b/src/wwwroot/docs/html-scripts.html
index effd4b3..9cd9385 100644
--- a/src/wwwroot/docs/html-scripts.html
+++ b/src/wwwroot/docs/html-scripts.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/introduction.html b/src/wwwroot/docs/introduction.html
index 83b1e63..3bda374 100644
--- a/src/wwwroot/docs/introduction.html
+++ b/src/wwwroot/docs/introduction.html
@@ -28,12 +28,12 @@
Quick walk through
{{ 'gfm/introduction/02.md' | githubMarkdown }}
-
Scripts only have access to script methods, blocks and arguments defined within its Context, which for an empty Context are
the comprehensive suite of safe Default Scripts and HTML Scripts.
+
Rendering Script Pages
@@ -51,6 +51,72 @@
Evaluating Scripts with return values
{{ 'gfm/introduction/10.md' | githubMarkdown }}
+
+Usage in .NET
+
+
+ To evaluate #Script in .NET you'll first create the ScriptContext containing all functionality and features
+ your Scripts have access to:
+
+
+{{ 'gfm/introduction/11.md' | githubMarkdown }}
+
+
+ Where you can customize the pure sandboxed ScriptContext your Script is executed
+ within by extending it with:
+
+
+
+ -
+ Arguments
+
+ -
+ Script Methods
+
+ -
+ Script Blocks
+
+ -
+ Filter Transformers
+
+ -
+ Script Plugins
+
+ -
+ Page Formats
+
+ -
+ Plugins
+
+
+
+Autowired using ScriptContext IOC
+
+
+ ScanTypes is useful for Autowiring instances of scripts created using ScriptContext's configured IOC
+ where they're also injected with any registered IOC dependencies and can be used to autowire
+ ScriptMethods,
+ ScriptBlocks and
+ Code Pages:
+
+
+{{ 'gfm/methods/07.md' | githubMarkdown }}
+
+
+ When the ScriptContext is initialized it will go through each Type and create an autowired instance of each Type
+ and register them in the ScriptMethods, ScriptBlocks and CodePages collections.
+
+
+
+ An alternative to registering a single Type is to register an entire Assembly, e.g:
+
+
+{{ 'gfm/methods/08.md' | githubMarkdown }}
+
+
+ Where it will search each Type in the Assembly for Script Methods and automatically register them.
+
+
Multi page Scripts
{{ "live-pages" | partial(
diff --git a/src/wwwroot/docs/model-view-controller.html b/src/wwwroot/docs/model-view-controller.html
index 067119c..9d7f6f7 100644
--- a/src/wwwroot/docs/model-view-controller.html
+++ b/src/wwwroot/docs/model-view-controller.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/mvc-netcore.html b/src/wwwroot/docs/mvc-netcore.html
index 321fa78..1682ebf 100644
--- a/src/wwwroot/docs/mvc-netcore.html
+++ b/src/wwwroot/docs/mvc-netcore.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/partials.html b/src/wwwroot/docs/partials.html
index a723818..81ca2b1 100644
--- a/src/wwwroot/docs/partials.html
+++ b/src/wwwroot/docs/partials.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/protected-scripts.html b/src/wwwroot/docs/protected-scripts.html
index 3ff5aa6..ca09a31 100644
--- a/src/wwwroot/docs/protected-scripts.html
+++ b/src/wwwroot/docs/protected-scripts.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/redis-scripts.html b/src/wwwroot/docs/redis-scripts.html
index 01601a7..1e0fab7 100644
--- a/src/wwwroot/docs/redis-scripts.html
+++ b/src/wwwroot/docs/redis-scripts.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/sandbox.html b/src/wwwroot/docs/sandbox.html
index b1d1202..bed8e3a 100644
--- a/src/wwwroot/docs/sandbox.html
+++ b/src/wwwroot/docs/sandbox.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/script-plugins.html b/src/wwwroot/docs/script-plugins.html
new file mode 100644
index 0000000..4c02a2c
--- /dev/null
+++ b/src/wwwroot/docs/script-plugins.html
@@ -0,0 +1,7 @@
+
+
+{{ 'gfm/script-plugins/01.md' | githubMarkdown }}
+
diff --git a/src/wwwroot/docs/servicestack-scripts.html b/src/wwwroot/docs/servicestack-scripts.html
index dbf579c..f6df9a8 100644
--- a/src/wwwroot/docs/servicestack-scripts.html
+++ b/src/wwwroot/docs/servicestack-scripts.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/sharp-apis.html b/src/wwwroot/docs/sharp-apis.html
index cbcb890..598a6b3 100644
--- a/src/wwwroot/docs/sharp-apis.html
+++ b/src/wwwroot/docs/sharp-apis.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/sharp-apps.html b/src/wwwroot/docs/sharp-apps.html
index 5e499e7..c4e4042 100644
--- a/src/wwwroot/docs/sharp-apps.html
+++ b/src/wwwroot/docs/sharp-apps.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/sharp-pages.html b/src/wwwroot/docs/sharp-pages.html
index 6f9c53c..d066b8a 100644
--- a/src/wwwroot/docs/sharp-pages.html
+++ b/src/wwwroot/docs/sharp-pages.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/docs/sharp-scripts.html b/src/wwwroot/docs/sharp-scripts.html
index cbff442..eba327d 100644
--- a/src/wwwroot/docs/sharp-scripts.html
+++ b/src/wwwroot/docs/sharp-scripts.html
@@ -1,6 +1,6 @@
{{#markdown}}
diff --git a/src/wwwroot/docs/transformers.html b/src/wwwroot/docs/transformers.html
index e9ba943..31e8f90 100644
--- a/src/wwwroot/docs/transformers.html
+++ b/src/wwwroot/docs/transformers.html
@@ -1,6 +1,6 @@
diff --git a/src/wwwroot/gfm/introduction/11.html b/src/wwwroot/gfm/introduction/11.html
new file mode 100644
index 0000000..7b1602e
--- /dev/null
+++ b/src/wwwroot/gfm/introduction/11.html
@@ -0,0 +1,12 @@
+
var context = new ScriptContext {
+ Args = { ... }, // Global Arguments available to all Scripts, Pages, Partials, etc
+ Plugins = { ... }, // Encapsulated Features, e.g. Markdown, Protected or ServiceStack Features
+ ScriptMethods = { ... }, // Additional Methods
+ ScriptBlocks = { ... }, // Additional Script Blocks
+ FilterTransformers = { ... }, // Additional Stream Transformers
+ PageFormats = { ... }, // Additional Text Document Formats
+
+ ScanTypes = { ... }, // Auto register Methods, Blocks and Code Page Types
+ ScanAssemblies = { ... }, // Auto register all Methods, Blocks and Code Page Types in Assembly
+}.Init();
+
\ No newline at end of file
diff --git a/src/wwwroot/gfm/introduction/11.md b/src/wwwroot/gfm/introduction/11.md
new file mode 100644
index 0000000..a4efc8e
--- /dev/null
+++ b/src/wwwroot/gfm/introduction/11.md
@@ -0,0 +1,13 @@
+```csharp
+var context = new ScriptContext {
+ Args = { ... }, // Global Arguments available to all Scripts, Pages, Partials, etc
+ Plugins = { ... }, // Encapsulated Features, e.g. Markdown, Protected or ServiceStack Features
+ ScriptMethods = { ... }, // Additional Methods
+ ScriptBlocks = { ... }, // Additional Script Blocks
+ FilterTransformers = { ... }, // Additional Stream Transformers
+ PageFormats = { ... }, // Additional Text Document Formats
+
+ ScanTypes = { ... }, // Auto register Methods, Blocks and Code Page Types
+ ScanAssemblies = { ... }, // Auto register all Methods, Blocks and Code Page Types in Assembly
+}.Init();
+```
diff --git a/src/wwwroot/gfm/methods/01.html b/src/wwwroot/gfm/methods/01.html
index 4909423..98bba72 100644
--- a/src/wwwroot/gfm/methods/01.html
+++ b/src/wwwroot/gfm/methods/01.html
@@ -1,2 +1,4 @@
-context.ScriptMethods.Insert(0, new MyScriptMethods());
+new ScriptContext {
+ InsertScriptMethods = { new MyScriptMethods() }
+}.Init();
\ No newline at end of file
diff --git a/src/wwwroot/gfm/methods/01.md b/src/wwwroot/gfm/methods/01.md
index 8493409..fbe13e0 100644
--- a/src/wwwroot/gfm/methods/01.md
+++ b/src/wwwroot/gfm/methods/01.md
@@ -1,3 +1,5 @@
```csharp
-context.ScriptMethods.Insert(0, new MyScriptMethods());
+new ScriptContext {
+ InsertScriptMethods = { new MyScriptMethods() }
+}.Init();
```
diff --git a/src/wwwroot/gfm/methods/09.md b/src/wwwroot/gfm/methods/09.md
index 8493409..fbe13e0 100644
--- a/src/wwwroot/gfm/methods/09.md
+++ b/src/wwwroot/gfm/methods/09.md
@@ -1,3 +1,5 @@
```csharp
-context.ScriptMethods.Insert(0, new MyScriptMethods());
+new ScriptContext {
+ InsertScriptMethods = { new MyScriptMethods() }
+}.Init();
```
diff --git a/src/wwwroot/gfm/script-plugins/01.html b/src/wwwroot/gfm/script-plugins/01.html
new file mode 100644
index 0000000..a46babb
--- /dev/null
+++ b/src/wwwroot/gfm/script-plugins/01.html
@@ -0,0 +1,179 @@
+Similar to using Plugins in ServiceStack to easily add functionality
+to a ServiceStack AppHost, you can encapsulate a suite of functionality that can be easily added to extend a
+ScriptContext with related functionality, e.g: You can add Markdown functionality to your scripts with:
+var context = new ScriptContext {
+ Plugins = {
+ new MarkdownScriptPlugin {
+ RegisterPageFormat = false
+ }
+ }
+}.Init();
+Which will register Markdown filter transformers, script method and script block with our ScriptContext:
+public class MarkdownScriptPlugin : IScriptPlugin
+{
+ public bool RegisterPageFormat { get; set; } = true;
+
+ public void Register(ScriptContext context)
+ {
+ if (RegisterPageFormat)
+ context.PageFormats.Add(new MarkdownPageFormat());
+
+ context.FilterTransformers["markdown"] = MarkdownPageFormat.TransformToHtml;
+
+ context.ScriptMethods.Add(new MarkdownScriptMethods());
+
+ ScriptConfig.DontEvaluateBlocksNamed.Add("markdown");
+
+ context.ScriptBlocks.Add(new MarkdownScriptBlock());
+ }
+}
+
+Creating Plugins
+Plugin are simply classes that implement IScriptPlugin interface and its Register() method to extend the ScriptContext
+with additional functionality we want to make available:
+public interface IScriptPlugin
+{
+ void Register(ScriptContext context);
+}
+Plugins can implement the interfaces below if they need to run custom logic before and after plugins are registered:
+// Run before plugins are loaded:
+public interface IScriptPluginBefore
+{
+ void BeforePluginsLoaded(ScriptContext context);
+}
+
+// Run after plugins are loaded:
+public interface IScriptPluginAfter
+{
+ void AfterPluginsLoaded(ScriptContext context);
+}
+
+Pre-registered Plugins in ScriptContext
+
+Pre-registered plugins are useful when you want to easily remove a pre-registered suite of functionality, e.g:
+var context = new ScriptContext {
+ //...
+ }
+ .RemovePlugins(x => x is DefaultScriptBlocks) // Remove default blocks
+ .RemovePlugins(x => x is HtmlScriptBlocks) // Remove all html blocks
+ .Init();
+
+DefaultScriptBlocks
+The Default Script Blocks contain all the statement functionality in #Script which includes:
+public class DefaultScriptBlocks : IScriptPlugin
+{
+ public void Register(ScriptContext context)
+ {
+ context.ScriptBlocks.AddRange(new ScriptBlock[] {
+ new IfScriptBlock(),
+ new EachScriptBlock(),
+ new RawScriptBlock(),
+ new CaptureScriptBlock(),
+ new PartialScriptBlock(),
+ new WithScriptBlock(),
+ new NoopScriptBlock(),
+ });
+ }
+}
+
+HtmlScriptBlocks
+The purpose of the HTML Script Blocks is to pack a suite of generically useful functionality commonly used when generating html.
+All html blocks inherit the same functionality with blocks registered for the most popular HTML elements, currently:
+public class HtmlScriptBlocks : IScriptPlugin
+{
+ /// <summary>
+ /// Usages: {{#ul {each:items, class:'nav'} }} <li>{{it}}</li> {{/ul}}
+ /// </summary>
+
+ public void Register(ScriptContext context)
+ {
+ context.ScriptBlocks.AddRange(new ScriptBlock[] {
+ new ScriptUlBlock(),
+ new ScriptOlBlock(),
+ new ScriptLiBlock(),
+ new ScriptDivBlock(),
+ new ScriptPBlock(),
+ new ScriptFormBlock(),
+ new ScriptInputBlock(),
+ new ScriptSelectBlock(),
+ new ScriptOptionBlock(),
+ new ScriptTextAreaBlock(),
+ new ScriptButtonBlock(),
+ new ScriptTableBlock(),
+ new ScriptTrBlock(),
+ new ScriptTdBlock(),
+ new ScriptTHeadBlock(),
+ new ScriptTBodyBlock(),
+ new ScriptTFootBlock(),
+ new ScriptDlBlock(),
+ new ScriptDtBlock(),
+ new ScriptDdBlock(),
+ new ScriptSpanBlock(),
+ new ScriptABlock(),
+ new ScriptImgBlock(),
+ new ScriptEmBlock(),
+ new ScriptBBlock(),
+ new ScriptIBlock(),
+ new ScriptStrongBlock(),
+ });
+ }
+}
+
+Pre-registered Plugins in SharpPagesFeature
+
+The SharpPagesFeature in ServiceStack.dll has access to more dependencies than ScriptContext and
+is able to pre-register more functionality by default including:
+
+ServiceStackScriptBlocks
+Containing the {{#minifyjs}}{{/minifyjs}}, {{#minifycss}}{{/minifycss}} and {{#minifyhtml}}{{/minifyhtml}} for minifying
+its contents:
+public class ServiceStackScriptBlocks : IScriptPlugin
+{
+ public void Register(ScriptContext context)
+ {
+ context.ScriptBlocks.AddRange(new ScriptBlock[] {
+ new MinifyJsScriptBlock(),
+ new MinifyCssScriptBlock(),
+ new MinifyHtmlScriptBlock(),
+ });
+ }
+}
+
+MarkdownScriptPlugin
+Adds Markdown supports to Sharp Pages by default:
+public class MarkdownScriptPlugin : IScriptPlugin
+{
+ public bool RegisterPageFormat { get; set; } = true;
+
+ public void Register(ScriptContext context)
+ {
+ if (RegisterPageFormat)
+ context.PageFormats.Add(new MarkdownPageFormat());
+
+ context.FilterTransformers["markdown"] = MarkdownPageFormat.TransformToHtml;
+
+ context.ScriptMethods.Add(new MarkdownScriptMethods());
+
+ ScriptConfig.DontEvaluateBlocksNamed.Add("markdown");
+
+ context.ScriptBlocks.Add(new MarkdownScriptBlock());
+ }
+}
+Added using:
+Plugins.Add(new MarkdownScriptPlugin { RegisterPageFormat = false });
+
+Available Plugins
+List of available plugins that's not pre-registered anywhere include:
+
+ProtectedScriptBlocks
+public class ProtectedScriptBlocks : IScriptPlugin
+{
+ public void Register(ScriptContext context)
+ {
+ context.ScriptBlocks.AddRange(new ScriptBlock[] {
+ new EvalScriptBlock(), // evalTemplate script method has same functionality
+ });
+ }
+}
+Although generally not required as evalTemplate script method registered by default has equivalent functionality.
+
\ No newline at end of file
diff --git a/src/wwwroot/gfm/script-plugins/01.md b/src/wwwroot/gfm/script-plugins/01.md
new file mode 100644
index 0000000..e1b7cc7
--- /dev/null
+++ b/src/wwwroot/gfm/script-plugins/01.md
@@ -0,0 +1,221 @@
+Similar to using [Plugins in ServiceStack](https://docs.servicestack.net/plugins) to easily add functionality
+to a ServiceStack `AppHost`, you can encapsulate a suite of functionality that can be easily added to extend a
+`ScriptContext` with related functionality, e.g: You can add **Markdown** functionality to your scripts with:
+
+```csharp
+var context = new ScriptContext {
+ Plugins = {
+ new MarkdownScriptPlugin {
+ RegisterPageFormat = false
+ }
+ }
+}.Init();
+```
+
+Which will register Markdown filter transformers, script method and script block with our `ScriptContext`:
+
+```csharp
+public class MarkdownScriptPlugin : IScriptPlugin
+{
+ public bool RegisterPageFormat { get; set; } = true;
+
+ public void Register(ScriptContext context)
+ {
+ if (RegisterPageFormat)
+ context.PageFormats.Add(new MarkdownPageFormat());
+
+ context.FilterTransformers["markdown"] = MarkdownPageFormat.TransformToHtml;
+
+ context.ScriptMethods.Add(new MarkdownScriptMethods());
+
+ ScriptConfig.DontEvaluateBlocksNamed.Add("markdown");
+
+ context.ScriptBlocks.Add(new MarkdownScriptBlock());
+ }
+}
+```
+
+### Creating Plugins
+
+Plugin are simply classes that implement `IScriptPlugin` interface and its `Register()` method to extend the `ScriptContext`
+with additional functionality we want to make available:
+
+```csharp
+public interface IScriptPlugin
+{
+ void Register(ScriptContext context);
+}
+```
+
+Plugins can implement the interfaces below if they need to run custom logic before and after plugins are registered:
+
+```csharp
+// Run before plugins are loaded:
+public interface IScriptPluginBefore
+{
+ void BeforePluginsLoaded(ScriptContext context);
+}
+
+// Run after plugins are loaded:
+public interface IScriptPluginAfter
+{
+ void AfterPluginsLoaded(ScriptContext context);
+}
+```
+
+## Pre-registered Plugins in `ScriptContext`
+
+Pre-registered plugins are useful when you want to easily **remove** a pre-registered suite of functionality, e.g:
+
+```csharp
+var context = new ScriptContext {
+ //...
+ }
+ .RemovePlugins(x => x is DefaultScriptBlocks) // Remove default blocks
+ .RemovePlugins(x => x is HtmlScriptBlocks) // Remove all html blocks
+ .Init();
+```
+
+### DefaultScriptBlocks
+
+The Default [Script Blocks](/docs/blocks) contain all the **statement** functionality in `#Script` which includes:
+
+```csharp
+public class DefaultScriptBlocks : IScriptPlugin
+{
+ public void Register(ScriptContext context)
+ {
+ context.ScriptBlocks.AddRange(new ScriptBlock[] {
+ new IfScriptBlock(),
+ new EachScriptBlock(),
+ new RawScriptBlock(),
+ new CaptureScriptBlock(),
+ new PartialScriptBlock(),
+ new WithScriptBlock(),
+ new NoopScriptBlock(),
+ });
+ }
+}
+```
+
+### HtmlScriptBlocks
+
+The purpose of the [HTML Script Blocks](/docs/blocks#html) is to pack a suite of generically useful functionality commonly used when generating html.
+All html blocks inherit the same functionality with blocks registered for the most popular HTML elements, currently:
+
+```csharp
+public class HtmlScriptBlocks : IScriptPlugin
+{
+ ///
+ /// Usages: {{#ul {each:items, class:'nav'} }} {{it}} {{/ul}}
+ ///
+
+ public void Register(ScriptContext context)
+ {
+ context.ScriptBlocks.AddRange(new ScriptBlock[] {
+ new ScriptUlBlock(),
+ new ScriptOlBlock(),
+ new ScriptLiBlock(),
+ new ScriptDivBlock(),
+ new ScriptPBlock(),
+ new ScriptFormBlock(),
+ new ScriptInputBlock(),
+ new ScriptSelectBlock(),
+ new ScriptOptionBlock(),
+ new ScriptTextAreaBlock(),
+ new ScriptButtonBlock(),
+ new ScriptTableBlock(),
+ new ScriptTrBlock(),
+ new ScriptTdBlock(),
+ new ScriptTHeadBlock(),
+ new ScriptTBodyBlock(),
+ new ScriptTFootBlock(),
+ new ScriptDlBlock(),
+ new ScriptDtBlock(),
+ new ScriptDdBlock(),
+ new ScriptSpanBlock(),
+ new ScriptABlock(),
+ new ScriptImgBlock(),
+ new ScriptEmBlock(),
+ new ScriptBBlock(),
+ new ScriptIBlock(),
+ new ScriptStrongBlock(),
+ });
+ }
+}
+```
+
+## Pre-registered Plugins in `SharpPagesFeature`
+
+The [SharpPagesFeature](/docs/sharp-pages) in `ServiceStack.dll` has access to more dependencies than `ScriptContext` and
+is able to pre-register more functionality by default including:
+
+### ServiceStackScriptBlocks
+
+Containing the `{{#minifyjs}}{{/minifyjs}}`, `{{#minifycss}}{{/minifycss}}` and `{{#minifyhtml}}{{/minifyhtml}}` for minifying
+its contents:
+
+```csharp
+public class ServiceStackScriptBlocks : IScriptPlugin
+{
+ public void Register(ScriptContext context)
+ {
+ context.ScriptBlocks.AddRange(new ScriptBlock[] {
+ new MinifyJsScriptBlock(),
+ new MinifyCssScriptBlock(),
+ new MinifyHtmlScriptBlock(),
+ });
+ }
+}
+```
+
+### MarkdownScriptPlugin
+
+Adds Markdown supports to [Sharp Pages](/docs/sharp-pages) by default:
+
+```csharp
+public class MarkdownScriptPlugin : IScriptPlugin
+{
+ public bool RegisterPageFormat { get; set; } = true;
+
+ public void Register(ScriptContext context)
+ {
+ if (RegisterPageFormat)
+ context.PageFormats.Add(new MarkdownPageFormat());
+
+ context.FilterTransformers["markdown"] = MarkdownPageFormat.TransformToHtml;
+
+ context.ScriptMethods.Add(new MarkdownScriptMethods());
+
+ ScriptConfig.DontEvaluateBlocksNamed.Add("markdown");
+
+ context.ScriptBlocks.Add(new MarkdownScriptBlock());
+ }
+}
+```
+
+Added using:
+
+```csharp
+Plugins.Add(new MarkdownScriptPlugin { RegisterPageFormat = false });
+```
+
+## Available Plugins
+
+List of available plugins that's not pre-registered anywhere include:
+
+### ProtectedScriptBlocks
+
+```csharp
+public class ProtectedScriptBlocks : IScriptPlugin
+{
+ public void Register(ScriptContext context)
+ {
+ context.ScriptBlocks.AddRange(new ScriptBlock[] {
+ new EvalScriptBlock(), // evalTemplate script method has same functionality
+ });
+ }
+}
+```
+
+Although generally not required as [evalTemplate script method](/docs/default-scripts#evalTemplate) registered by default has equivalent functionality.
\ No newline at end of file
From 64b354d6096aaae506739cacafb3c507f72b7b26 Mon Sep 17 00:00:00 2001
From: Demis Bellot
Date: Fri, 5 Apr 2019 23:10:48 -0400
Subject: [PATCH 025/206] Update 11.md
---
src/wwwroot/gfm/introduction/11.html | 18 +++++++++---------
src/wwwroot/gfm/introduction/11.md | 18 +++++++++---------
2 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/src/wwwroot/gfm/introduction/11.html b/src/wwwroot/gfm/introduction/11.html
index 7b1602e..afc1459 100644
--- a/src/wwwroot/gfm/introduction/11.html
+++ b/src/wwwroot/gfm/introduction/11.html
@@ -1,12 +1,12 @@
var context = new ScriptContext {
- Args = { ... }, // Global Arguments available to all Scripts, Pages, Partials, etc
- Plugins = { ... }, // Encapsulated Features, e.g. Markdown, Protected or ServiceStack Features
- ScriptMethods = { ... }, // Additional Methods
- ScriptBlocks = { ... }, // Additional Script Blocks
- FilterTransformers = { ... }, // Additional Stream Transformers
- PageFormats = { ... }, // Additional Text Document Formats
-
- ScanTypes = { ... }, // Auto register Methods, Blocks and Code Page Types
- ScanAssemblies = { ... }, // Auto register all Methods, Blocks and Code Page Types in Assembly
+ Args = { ... }, // Global Arguments available to all Scripts, Pages, Partials, etc
+ ScriptMethods = { ... }, // Additional Methods
+ ScriptBlocks = { ... }, // Additional Script Blocks
+ FilterTransformers = { .. }, // Additional Stream Transformers
+ PageFormats = { ... }, // Additional Text Document Formats
+ Plugins = { ... }, // Encapsulated Features e.g. Markdown, Protected or ServiceStack Features
+
+ ScanTypes = { ... }, // Auto register Methods, Blocks and Code Page Types
+ ScanAssemblies = { ... }, // Auto register all Methods, Blocks and Code Page Types in Assembly
}.Init();
\ No newline at end of file
diff --git a/src/wwwroot/gfm/introduction/11.md b/src/wwwroot/gfm/introduction/11.md
index a4efc8e..9f0bcd9 100644
--- a/src/wwwroot/gfm/introduction/11.md
+++ b/src/wwwroot/gfm/introduction/11.md
@@ -1,13 +1,13 @@
```csharp
var context = new ScriptContext {
- Args = { ... }, // Global Arguments available to all Scripts, Pages, Partials, etc
- Plugins = { ... }, // Encapsulated Features, e.g. Markdown, Protected or ServiceStack Features
- ScriptMethods = { ... }, // Additional Methods
- ScriptBlocks = { ... }, // Additional Script Blocks
- FilterTransformers = { ... }, // Additional Stream Transformers
- PageFormats = { ... }, // Additional Text Document Formats
-
- ScanTypes = { ... }, // Auto register Methods, Blocks and Code Page Types
- ScanAssemblies = { ... }, // Auto register all Methods, Blocks and Code Page Types in Assembly
+ Args = { ... }, // Global Arguments available to all Scripts, Pages, Partials, etc
+ ScriptMethods = { ... }, // Additional Methods
+ ScriptBlocks = { ... }, // Additional Script Blocks
+ FilterTransformers = { .. }, // Additional Stream Transformers
+ PageFormats = { ... }, // Additional Text Document Formats
+ Plugins = { ... }, // Encapsulated Features e.g. Markdown, Protected or ServiceStack Features
+
+ ScanTypes = { ... }, // Auto register Methods, Blocks and Code Page Types
+ ScanAssemblies = { ... }, // Auto register all Methods, Blocks and Code Page Types in Assembly
}.Init();
```
From ba6645f28217089028f371bc30cbfcce5ed453f5 Mon Sep 17 00:00:00 2001
From: Demis Bellot
Date: Tue, 9 Apr 2019 11:01:40 -0400
Subject: [PATCH 026/206] Update index.html
---
src/wwwroot/index.html | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/wwwroot/index.html b/src/wwwroot/index.html
index 1877ee3..9cd6cce 100644
--- a/src/wwwroot/index.html
+++ b/src/wwwroot/index.html
@@ -26,9 +26,11 @@ Popular Use Cases
Sharp Scripts
- The easiest way to get started is to run a Sharp Script in
- watch mode
+ The easiest way to get started is to use the dotnet web tool
+ to run a Sharp Script in watch mode
by creating a text file with the .ss extension and running it with web watch <name>.ss.
+
+
Scripts have access to all built-in Scripts and ServiceStack's which makes it easy to create scripts
that run against
all supported RDBMS's
From 4ef1b5411373a410315a72d1a0bdb2071a0ae25a Mon Sep 17 00:00:00 2001
From: Demis Bellot
Date: Tue, 16 Apr 2019 21:30:03 -0400
Subject: [PATCH 027/206] Update GA
---
src/wwwroot/_layout.html | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/src/wwwroot/_layout.html b/src/wwwroot/_layout.html
index 553df41..901dd03 100644
--- a/src/wwwroot/_layout.html
+++ b/src/wwwroot/_layout.html
@@ -62,14 +62,15 @@ {{ title }}
{{ scripts | ifExists }}
-
-
+
+
+
\ No newline at end of file
From 1253d1944a2f3a55ec3ce7fb57d1d09a315d2e75 Mon Sep 17 00:00:00 2001
From: Demis Bellot
Date: Tue, 16 Apr 2019 22:00:44 -0400
Subject: [PATCH 028/206] Update screenshot.png
---
src/wwwroot/assets/img/screenshot.png | Bin 773733 -> 973151 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
diff --git a/src/wwwroot/assets/img/screenshot.png b/src/wwwroot/assets/img/screenshot.png
index 42aba2675462108c7d27e10da861b0c8ae7279b1..aa54607aa1049bb10d0893dcf4cdd519676d0172 100644
GIT binary patch
literal 973151
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