diff --git a/.env b/.env
new file mode 100644
index 00000000..fefc515a
--- /dev/null
+++ b/.env
@@ -0,0 +1 @@
+NULLSTACK_SERVER_PORT=5000
\ No newline at end of file
diff --git a/.github/workflows/ssg-build.yml b/.github/workflows/ssg-build.yml
new file mode 100644
index 00000000..f32d4a95
--- /dev/null
+++ b/.github/workflows/ssg-build.yml
@@ -0,0 +1,42 @@
+# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
+
+name: SSG Build to Deploy
+
+on:
+ push:
+ branches: [ master ]
+ workflow_dispatch:
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ # cache the dependencies from any node_modules directory
+ - name: Cache dependencies
+ uses: actions/cache@v2
+ with:
+ path: |
+ **/node_modules
+ **/package-lock.json
+ **/.production
+ key: node_modules-${{ hashFiles('**/package.json') }}
+
+ - name: Install deps
+ run: npm install
+
+ - name: Build
+ run: npx nullstack build --mode=ssg --output=docs
+
+ - name: Send to Github
+ run: |
+ git branch gh-pages -f
+ git checkout gh-pages
+ touch ./docs/.nojekyll
+ git add docs -f
+ git config --global user.name "NullstackDeployer"
+ git config --global user.email "nullstack@deployer.ci"
+ git commit -m ":rocket: New SSG Build"
+ git push -u origin gh-pages -f
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index e0cf2191..81cf24c6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,8 @@
node_modules
package-lock.json
+yarn.lock
.development
-.production
\ No newline at end of file
+.production
+public/pt-BR.json
+public/en-US.json
+docs
diff --git a/README.br.md b/README.br.md
new file mode 100644
index 00000000..2ff90cac
--- /dev/null
+++ b/README.br.md
@@ -0,0 +1,21 @@
+# Documentação do Nullstack
+
+
+
+## Como executar este projeto
+
+Instale as dependências:
+
+`npm install`
+
+Rode a aplicação no modo de desenvolvimento:
+
+`npm start`
+
+Abra [http://localhost:5000](http://localhost:5000) para vê-lo no navegador.
+
+## Aprenda mais sobre o Nullstack
+
+[Leia a documentação](https://nullstack.app/pt-br/comecando)
+
+ Read in English
\ No newline at end of file
diff --git a/README.md b/README.md
index 3e142c06..196e83fb 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,21 @@
+# Nullstack Documentation
+
-## Welcome to Nullstack
+## How to run this Project
+
+Install the dependencies:
+
+`npm install`
+
+Runs the app in the development mode:
+
+`npm start`
+
+Open [http://localhost:5000](http://localhost:5000) to view it in the browser.
+
+## Learn more about Nullstack
+
+[Read the documentation](https://nullstack.app/getting-started)
-[Read the documentation](https://github.com/nullstack/nullstack)
\ No newline at end of file
+ Leia em Português
\ No newline at end of file
diff --git a/articles/about.md b/articles/about.md
deleted file mode 100644
index c553a0e8..00000000
--- a/articles/about.md
+++ /dev/null
@@ -1,93 +0,0 @@
----
-title: Why Nullstack Exists
-description: The sole purpose of Nullstack is to simplify the development by eliminating glue code and letting you focus on the logic of your application
----
-
-The sole purpose of Nullstack is to simplify the development by eliminating glue code and letting you focus on the logic of your application.
-
-It was created keeping in mind programmers used to developing entire systems alone, but it is easily scalable to small or even big teams, as long as each programmer knows the flow of the feature they are to develop.
-
-## The Stack
-
-With the stack of technologies used in the web nowadays, the most common flow is something like this:
-
-- Front-end uses a reducer over a context that calls a fetcher;
-- This fetcher brings generic information over a RESTful API;
-- The RESTful API calls a server route, which calls a controller, which takes the information of a model and resolves it into a serializer;
-- If you need more than one resource, this process should be repeated until all resources are fetched;
-- After all the data has been fetched, only then should the front-end be able to use it;
-- Reason about how to deal with server-side render and hydration of the steps above;
-
-Note that all you wanted was to show something from the database into a view. With Nullstack, that’s all you need to concern yourself with. Everything else is “glue code” and the framework should take care of it for you.
-
-## Feature-driven
-
-If you’re used to working on more than one project at a time or even if you just happen to have to give sporadic maintenance to a lot of your old projects, you might have stumbled upon this scenario: you don’t remember exactly where in your code is the logic you’re trying to fix or improve.
-
-You might have a hook whose dependencies are local variables initialized with a redux state, which was stored at some point by an action declared somewhere in your source tree and called in who knows where.
-
-If everything pertaining to a single feature were to be in the same file, maybe you wouldn’t need to reverse engineer your own code every time you need to update or fix something.
-
-Putting everything in a single file may sound messy at a glance, but remember that you are the one who decides the granularity of this division.
-
-A "feature" might be an entire register form or something as small as a button that does some verifications before letting you submit that form. It’s entirely up to you, and since each component is as complete as an entire feature, you could call this button or even the entire form on other pages in your application. This leads us to **True Componentization and Code Reusability**.
-
-## Componentization and Code Reusability
-
-Components in Nullstack are self-sufficient.
-
-Most frameworks are specialized in a single layer, meaning that any component will be only as complete as its framework. When exporting a Nullstack component, all the code needed to run the feature is going to be together, without the need of allocating the other layers separately.
-
-As a side effect, entire applications can be used as components, and mounted in other applications as engines.
-
-## Why object-oriented instead of functional
-
-At first glance, classes may look more verbose than the trendy functional components.
-This section will explain the reasons that lead us to favor classes in the development of Nullstack.
-
-The reasons are actually connected to some core principles of Nullstack, being:
-
-### Everything as Vanilla as Possible
-
-We didn’t want to introduce a “Nullstack way” of doing things and wanted it to be accessible to anyone with some Javascript knowledge.
-
-That being said, the first big problem was to address state management in a vanilla Javascript way. Supporting functional components would require a solution similar to the hooks of React.js that would be considered a mannerism of the framework.
-
-Since we opted out of immutability as a framework constraint, we are allowed to use the native way of setting simple variables. This removes the complexity of state management that created the need of third-party state management libraries in the first place.
-
-### No Glue Code or “Batteries Included”
-
-Nullstack borrows the concept of “battery-included” from Ember.js, but allows you to change batteries. Everything you need to make an application should be part of the framework, and still be flexible.
-
-A framework should do the heavy lifting and a programmer should focus on his own application.
-For this reason, all you have to do is to declare your classes and let Nullstack instantiate them for you. That way, we removed the most painful aspect of dealing with classes while maintaining all of the advantages of them.
-
-### Having a safe escape route
-
-Object-oriented versus functional is not a new topic, and lately the former seems to be bullied out of most frameworks, leaving no place for developers that enjoy this pattern.
-
-Admittedly classes took too long to be standardized into Javascript and the delay might have caused some traumatic bad implementations along the way.
-
-While object-oriented programming might not be the best solution for every problem, Nullstack allows you to import functions freely and use them in the moments when they should be the weapon of choice.
-
-## Why dependency injection instead of modularity
-
-Nullstack context uses the dependency injection pattern, which means that everything you need can be requested from the framework at the signature level of the function.
-
-The context is a horizontally scoped object that is injected in all of your function calls. The non-hierarchical nature of this pattern allows you to easily move around your component's logic as your application grows, while still avoiding problems like props drilling or filling your view layer with store declarations.
-
-This has two major advantages:
-
-- You see the dependencies of your code at a function level instead of having them all imported on top of the file.
-
-- The framework is able to give you the most precise information about the specific environment for that function call.
-
-## Developer Happiness
-
-The generated application is enough to have a PWA without thinking about boilerplates, but you are completely free to override the default behavior of each moving piece.
-
-A borrowed concept from Ruby is developer happiness. Nullstack aims to ease the developer’s life by simplifying everything possible, but without hiding things from you.
-
-The first developers we wanted to make happy are ourselves. We made Nullstack because we had fun in the process. It started as a simple prototype on top of React.js and we got carried away, each time making it more enjoyable for us until it became its own thing.
-
-We hope you enjoy using Nullstack as much as we do because that's what keeps this project going forward.
diff --git a/articles/application-startup.md b/articles/application-startup.md
deleted file mode 100644
index d64d51f4..00000000
--- a/articles/application-startup.md
+++ /dev/null
@@ -1,50 +0,0 @@
----
-title: Application Startup
-description: The start function will run only once when your application is booted and is a good place for setting up your server context
----
-
-The index.js file at your application root is responsible for starting your application.
-
-When you run the application with *npm start* or *node .production/server.js* the index will call the start function in your *src/Application.js*.
-
-The start function will run only once when your application is booted and is a good place for setting up your [server context](/context).
-
-```jsx
-import Nullstack from 'nullstack';
-import database from './database';
-
-class Application extends Nullstack {
-
- static async start(context) {
- context.database = database;
- }
-
-}
-
-export default Application;
-```
-
-## Dependency startup pattern
-
-A nice pattern to work with dependencies that require startup time configurations is to define a start function in the dependency and call it in the Application start function passing the [server context](/context).
-
-```jsx
-import Nullstack from 'nullstack';
-import Dependency from './Dependency';
-
-class Application extends Nullstack {
-
- static async start(context) {
- Dependency.start(context);
- }
-
-}
-
-export default Application;
-```
-
-> 🔒 Server functions with the name starting with "start" (and optionally followed by an uppercase letter) do not generate an API endpoint to avoid malicious context flooding.
-
-## Next step
-
-⚔ Learn about the [context data](/context-data).
\ No newline at end of file
diff --git a/articles/context-data.md b/articles/context-data.md
deleted file mode 100644
index 9181ddd4..00000000
--- a/articles/context-data.md
+++ /dev/null
@@ -1,62 +0,0 @@
----
-title: Context Data
-description: The data is an object in the framework store part of your context and gives you information about the element dataset.
----
-
-The data is an object in the framework store part of your context and gives you information about the element dataset.
-
-You can use this key to avoid polluting your DOM with invalid attributes.
-
-> 💡 This helps Nullstack set attributes without wasting time validating them.
-
-This key is *readonly* and available only in the *client* context.
-
-Any *data-\** attributes will receive a respective camelized key on the data object.
-
-You can assign data attributes both via data-* and a data key that accepts an object with camelized keys.
-
-The kebab version is also available in the context.
-
-```jsx
-import Nullstack from 'nullstack';
-
-class ContextData extends Nullstack {
-
- count = 1;
-
- calculate({data}) {
- this.count = this.count * data.multiply + data.sum;
- }
-
- renderInner(context) {
- const {data} = context;
- return (
-
I am in the client
} - {environment.server &&I am in the server
} - {environment.development &&I am in development mode
} - {environment.production &&I am in production mode
} - {environment.static &&I am a static site
} -My key is {environment.key}
-{page.description}
-Perhaps you want to learn abouthow to make a 404 page with Nullstack?
If you are looking for something else, you shouldread the documentation.
Perhaps you want to learn abouthow to make a 404 page with Nullstack?
If you are looking for something else, you shouldread the documentation.
The sole purpose of Nullstack is to simplify the development by eliminating glue code and letting you focus on the logic of your application.
-It was created keeping in mind programmers used to developing entire systems alone, but it is easily scalable to small or even big teams, as long as each programmer knows the flow of the feature they are to develop.
-With the stack of technologies used in the web nowadays, the most common flow is something like this:
-Note that all you wanted was to show something from the database into a view. With Nullstack, that’s all you need to concern yourself with. Everything else is “glue code” and the framework should take care of it for you.
-If you’re used to working on more than one project at a time or even if you just happen to have to give sporadic maintenance to a lot of your old projects, you might have stumbled upon this scenario: you don’t remember exactly where in your code is the logic you’re trying to fix or improve.
-You might have a hook whose dependencies are local variables initialized with a redux state, which was stored at some point by an action declared somewhere in your source tree and called in who knows where.
-If everything pertaining to a single feature were to be in the same file, maybe you wouldn’t need to reverse engineer your own code every time you need to update or fix something.
-Putting everything in a single file may sound messy at a glance, but remember that you are the one who decides the granularity of this division.
-A "feature" might be an entire register form or something as small as a button that does some verifications before letting you submit that form. It’s entirely up to you, and since each component is as complete as an entire feature, you could call this button or even the entire form on other pages in your application. This leads us to True Componentization and Code Reusability.
-Components in Nullstack are self-sufficient.
-Most frameworks are specialized in a single layer, meaning that any component will be only as complete as its framework. When exporting a Nullstack component, all the code needed to run the feature is going to be together, without the need of allocating the other layers separately.
-As a side effect, entire applications can be used as components, and mounted in other applications as engines.
-At first glance, classes may look more verbose than the trendy functional components. -This section will explain the reasons that lead us to favor classes in the development of Nullstack.
-The reasons are actually connected to some core principles of Nullstack, being:
-We didn’t want to introduce a “Nullstack way” of doing things and wanted it to be accessible to anyone with some Javascript knowledge.
-That being said, the first big problem was to address state management in a vanilla Javascript way. Supporting functional components would require a solution similar to the hooks of React.js that would be considered a mannerism of the framework.
-Since we opted out of immutability as a framework constraint, we are allowed to use the native way of setting simple variables. This removes the complexity of state management that created the need of third-party state management libraries in the first place.
-Nullstack borrows the concept of “battery-included” from Ember.js, but allows you to change batteries. Everything you need to make an application should be part of the framework, and still be flexible.
-A framework should do the heavy lifting and a programmer should focus on his own application. -For this reason, all you have to do is to declare your classes and let Nullstack instantiate them for you. That way, we removed the most painful aspect of dealing with classes while maintaining all of the advantages of them.
-Object-oriented versus functional is not a new topic, and lately the former seems to be bullied out of most frameworks, leaving no place for developers that enjoy this pattern.
-Admittedly classes took too long to be standardized into Javascript and the delay might have caused some traumatic bad implementations along the way.
-While object-oriented programming might not be the best solution for every problem, Nullstack allows you to import functions freely and use them in the moments when they should be the weapon of choice.
-Nullstack context uses the dependency injection pattern, which means that everything you need can be requested from the framework at the signature level of the function.
-The context is a horizontally scoped object that is injected in all of your function calls. The non-hierarchical nature of this pattern allows you to easily move around your component's logic as your application grows, while still avoiding problems like props drilling or filling your view layer with store declarations.
-This has two major advantages:
-You see the dependencies of your code at a function level instead of having them all imported on top of the file.
The framework is able to give you the most precise information about the specific environment for that function call.
The generated application is enough to have a PWA without thinking about boilerplates, but you are completely free to override the default behavior of each moving piece.
-A borrowed concept from Ruby is developer happiness. Nullstack aims to ease the developer’s life by simplifying everything possible, but without hiding things from you.
-The first developers we wanted to make happy are ourselves. We made Nullstack because we had fun in the process. It started as a simple prototype on top of React.js and we got carried away, each time making it more enjoyable for us until it became its own thing.
-We hope you enjoy using Nullstack as much as we do because that's what keeps this project going forward.
-The sole purpose of Nullstack is to simplify the development by eliminating glue code and letting you focus on the logic of your application.
\nIt was created keeping in mind programmers used to developing entire systems alone, but it is easily scalable to small or even big teams, as long as each programmer knows the flow of the feature they are to develop.
\nWith the stack of technologies used in the web nowadays, the most common flow is something like this:
\nNote that all you wanted was to show something from the database into a view. With Nullstack, that’s all you need to concern yourself with. Everything else is “glue code” and the framework should take care of it for you.
\nIf you’re used to working on more than one project at a time or even if you just happen to have to give sporadic maintenance to a lot of your old projects, you might have stumbled upon this scenario: you don’t remember exactly where in your code is the logic you’re trying to fix or improve.
\nYou might have a hook whose dependencies are local variables initialized with a redux state, which was stored at some point by an action declared somewhere in your source tree and called in who knows where.
\nIf everything pertaining to a single feature were to be in the same file, maybe you wouldn’t need to reverse engineer your own code every time you need to update or fix something.
\nPutting everything in a single file may sound messy at a glance, but remember that you are the one who decides the granularity of this division.
\nA "feature" might be an entire register form or something as small as a button that does some verifications before letting you submit that form. It’s entirely up to you, and since each component is as complete as an entire feature, you could call this button or even the entire form on other pages in your application. This leads us to True Componentization and Code Reusability.
\nComponents in Nullstack are self-sufficient.
\nMost frameworks are specialized in a single layer, meaning that any component will be only as complete as its framework. When exporting a Nullstack component, all the code needed to run the feature is going to be together, without the need of allocating the other layers separately.
\nAs a side effect, entire applications can be used as components, and mounted in other applications as engines.
\nAt first glance, classes may look more verbose than the trendy functional components.\nThis section will explain the reasons that lead us to favor classes in the development of Nullstack.
\nThe reasons are actually connected to some core principles of Nullstack, being:
\nWe didn’t want to introduce a “Nullstack way” of doing things and wanted it to be accessible to anyone with some Javascript knowledge.
\nThat being said, the first big problem was to address state management in a vanilla Javascript way. Supporting functional components would require a solution similar to the hooks of React.js that would be considered a mannerism of the framework.
\nSince we opted out of immutability as a framework constraint, we are allowed to use the native way of setting simple variables. This removes the complexity of state management that created the need of third-party state management libraries in the first place.
\nNullstack borrows the concept of “battery-included” from Ember.js, but allows you to change batteries. Everything you need to make an application should be part of the framework, and still be flexible.
\nA framework should do the heavy lifting and a programmer should focus on his own application.\nFor this reason, all you have to do is to declare your classes and let Nullstack instantiate them for you. That way, we removed the most painful aspect of dealing with classes while maintaining all of the advantages of them.
\nObject-oriented versus functional is not a new topic, and lately the former seems to be bullied out of most frameworks, leaving no place for developers that enjoy this pattern.
\nAdmittedly classes took too long to be standardized into Javascript and the delay might have caused some traumatic bad implementations along the way.
\nWhile object-oriented programming might not be the best solution for every problem, Nullstack allows you to import functions freely and use them in the moments when they should be the weapon of choice.
\nNullstack context uses the dependency injection pattern, which means that everything you need can be requested from the framework at the signature level of the function.
\nThe context is a horizontally scoped object that is injected in all of your function calls. The non-hierarchical nature of this pattern allows you to easily move around your component's logic as your application grows, while still avoiding problems like props drilling or filling your view layer with store declarations.
\nThis has two major advantages:
\nYou see the dependencies of your code at a function level instead of having them all imported on top of the file.
The framework is able to give you the most precise information about the specific environment for that function call.
The generated application is enough to have a PWA without thinking about boilerplates, but you are completely free to override the default behavior of each moving piece.
\nA borrowed concept from Ruby is developer happiness. Nullstack aims to ease the developer’s life by simplifying everything possible, but without hiding things from you.
\nThe first developers we wanted to make happy are ourselves. We made Nullstack because we had fun in the process. It started as a simple prototype on top of React.js and we got carried away, each time making it more enjoyable for us until it became its own thing.
\nWe hope you enjoy using Nullstack as much as we do because that's what keeps this project going forward.
\n","description":"The sole purpose of Nullstack is to simplify the development by eliminating glue code and letting you focus on the logic of your application"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Why Nullstack Exists - Nullstack","description":"The sole purpose of Nullstack is to simplify the development by eliminating glue code and letting you focus on the logic of your application"}} \ No newline at end of file diff --git a/docs/application-startup/index.html b/docs/application-startup/index.html deleted file mode 100644 index d93be04e..00000000 --- a/docs/application-startup/index.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - -The index.js file at your application root is responsible for starting your application.
-When you run the application with npm start or node .production/server.js the index will call the start function in your src/Application.js.
-The start function will run only once when your application is booted and is a good place for setting up your server context.
-import Nullstack from 'nullstack';
-import database from './database';
-
-class Application extends Nullstack {
-
- static async start(context) {
- context.database = database;
- }
-
-}
-
-export default Application;
-
-A nice pattern to work with dependencies that require startup time configurations is to define a start function in the dependency and call it in the Application start function passing the server context.
-import Nullstack from 'nullstack';
-import Dependency from './Dependency';
-
-class Application extends Nullstack {
-
- static async start(context) {
- Dependency.start(context);
- }
-
-}
-
-export default Application;
-
---🔒 Server functions with the name starting with "start" (and optionally followed by an uppercase letter) do not generate an API endpoint to avoid malicious context flooding.
-
⚔ Learn about the context data.
-The index.js file at your application root is responsible for starting your application.
\nWhen you run the application with npm start or node .production/server.js the index will call the start function in your src/Application.js.
\nThe start function will run only once when your application is booted and is a good place for setting up your server context.
\nimport Nullstack from 'nullstack';\nimport database from './database';\n\nclass Application extends Nullstack {\n\n static async start(context) {\n context.database = database;\n }\n\n}\n\nexport default Application;\n\nA nice pattern to work with dependencies that require startup time configurations is to define a start function in the dependency and call it in the Application start function passing the server context.
\nimport Nullstack from 'nullstack';\nimport Dependency from './Dependency';\n\nclass Application extends Nullstack {\n\n static async start(context) {\n Dependency.start(context);\n }\n\n}\n\nexport default Application;\n\n\n\n🔒 Server functions with the name starting with "start" (and optionally followed by an uppercase letter) do not generate an API endpoint to avoid malicious context flooding.
\n
⚔ Learn about the context data.
\n","description":"The start function will run only once when your application is booted and is a good place for setting up your server context"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Application Startup - Nullstack","description":"The start function will run only once when your application is booted and is a good place for setting up your server context"}} \ No newline at end of file diff --git a/docs/client-f0cee0769c64977a287dddeaae84c7f6.css b/docs/client-f0cee0769c64977a287dddeaae84c7f6.css deleted file mode 100644 index e6d5dd2a..00000000 --- a/docs/client-f0cee0769c64977a287dddeaae84c7f6.css +++ /dev/null @@ -1,4 +0,0 @@ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{margin:0.67em 0}a{background-color:transparent}b,strong{font-weight:bolder}img{border-style:none}[type="submit"]{-webkit-appearance:button}[type="submit"]::-moz-focus-inner{border-style:none;padding:0}[type="submit"]:-moz-focusring{outline:1px dotted ButtonText}blockquote,h1,h2,h3,h4,figure,p,pre{margin:0}ul{list-style:none;margin:0;padding:0}html{line-height:1.2}*,::before,::after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e2e8f0}img{border-style:solid}a{color:inherit;text-decoration:inherit}img,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}body *{box-sizing:border-box}:root{--spacer: 0.25rem}@media (min-width: 0px){.m1l{margin-left:calc(var(--spacer) * 1)}.m1y{margin-top:calc(var(--spacer) * 1)}.m1b,.m1y{margin-bottom:calc(var(--spacer) * 1)}.m2x{margin-left:calc(var(--spacer) * 2)}.m2x{margin-right:calc(var(--spacer) * 2)}.m2t{margin-top:calc(var(--spacer) * 2)}.m2b{margin-bottom:calc(var(--spacer) * 2)}.m3y{margin-top:calc(var(--spacer) * 3)}.m3b,.m3y{margin-bottom:calc(var(--spacer) * 3)}.m4t,.m4y{margin-top:calc(var(--spacer) * 4)}.m4b,.m4y{margin-bottom:calc(var(--spacer) * 4)}.m5t{margin-top:calc(var(--spacer) * 5)}.m6y{margin-top:calc(var(--spacer) * 6)}.m6b,.m6y{margin-bottom:calc(var(--spacer) * 6)}.m8t{margin-top:calc(var(--spacer) * 8)}.m8b{margin-bottom:calc(var(--spacer) * 8)}.m10y{margin-top:calc(var(--spacer) * 10)}.m10b,.m10y{margin-bottom:calc(var(--spacer) * 10)}.m12b{margin-bottom:calc(var(--spacer) * 12)}.m14t{margin-top:calc(var(--spacer) * 14)}.m20t{margin-top:calc(var(--spacer) * 20)}.p1{padding:calc(var(--spacer) * 1)}.p1l,.p1x{padding-left:calc(var(--spacer) * 1)}.p1x{padding-right:calc(var(--spacer) * 1)}.p1y{padding-top:calc(var(--spacer) * 1)}.p1b,.p1y{padding-bottom:calc(var(--spacer) * 1)}.p2{padding:calc(var(--spacer) * 2)}.p2y{padding-top:calc(var(--spacer) * 2)}.p2y{padding-bottom:calc(var(--spacer) * 2)}.p3y{padding-top:calc(var(--spacer) * 3)}.p3y{padding-bottom:calc(var(--spacer) * 3)}.p4{padding:calc(var(--spacer) * 4)}.p4x{padding-left:calc(var(--spacer) * 4)}.p4x{padding-right:calc(var(--spacer) * 4)}.p4t,.p4y{padding-top:calc(var(--spacer) * 4)}.p4y{padding-bottom:calc(var(--spacer) * 4)}.p8y{padding-top:calc(var(--spacer) * 8)}.p8y{padding-bottom:calc(var(--spacer) * 8)}.p10t,.p10y{padding-top:calc(var(--spacer) * 10)}.p10y{padding-bottom:calc(var(--spacer) * 10)}.p20y{padding-top:calc(var(--spacer) * 20)}.p20y{padding-bottom:calc(var(--spacer) * 20)}}@media (max-width: 768px){.sm\-m1y{margin-top:calc(var(--spacer) * 1)}.sm\-m1y{margin-bottom:calc(var(--spacer) * 1)}.sm\-m3t{margin-top:calc(var(--spacer) * 3)}.sm\-m10t{margin-top:calc(var(--spacer) * 10)}.sm\-p2l,.sm\-p2x{padding-left:calc(var(--spacer) * 2)}.sm\-p2x{padding-right:calc(var(--spacer) * 2)}.sm\-p4{padding:calc(var(--spacer) * 4)}.sm\-p4x{padding-left:calc(var(--spacer) * 4)}.sm\-p4x{padding-right:calc(var(--spacer) * 4)}.sm\-p4t{padding-top:calc(var(--spacer) * 4)}.sm\-p10t,.sm\-p10y{padding-top:calc(var(--spacer) * 10)}.sm\-p10y{padding-bottom:calc(var(--spacer) * 10)}}@media (max-width: 992px){.md\-p2x{padding-left:calc(var(--spacer) * 2)}.md\-p2x{padding-right:calc(var(--spacer) * 2)}}@media (min-width: 768px){.md\+m2x{margin-left:calc(var(--spacer) * 2)}.md\+m2x{margin-right:calc(var(--spacer) * 2)}.md\+m20t{margin-top:calc(var(--spacer) * 20)}.md\+p3l{padding-left:calc(var(--spacer) * 3)}.md\+p10l{padding-left:calc(var(--spacer) * 10)}.md\+p10y{padding-top:calc(var(--spacer) * 10)}.md\+p10b,.md\+p10y{padding-bottom:calc(var(--spacer) * 10)}.md\+p20t,.md\+p20y{padding-top:calc(var(--spacer) * 20)}.md\+p20y{padding-bottom:calc(var(--spacer) * 20)}}@media (max-width: 1200px){.lg\-p2x{padding-left:calc(var(--spacer) * 2)}.lg\-p2x{padding-right:calc(var(--spacer) * 2)}}@media (min-width: 0px){.pftl{position:fixed;left:0;top:0}.pabl{position:absolute;left:0;bottom:0}.prtl{position:relative;left:0;top:0}}:root{--box-shadow: 2px}@media (min-width: 0px){.bs2{box-shadow:0 0px calc(2 * var(--box-shadow)) 0 rgba(0,0,0,0.2)}}@media (min-width: 0px){.z24{z-index:2400}}@media (min-width: 0px){.off{display:none !important}}@media (max-width: 768px){.sm\-off{display:none !important}}@media (min-width: 768px){.md\+off{display:none !important}}.x{width:100%}@media (min-width: 992px){.x{max-width:960px;margin-left:auto;margin-right:auto}}@media (min-width: 1200px){.x{max-width:1140px;margin-left:auto;margin-right:auto}}@media (min-width: 0px){.xl{display:flex;flex-wrap:wrap;justify-content:flex-start;text-align:left}.xx{display:flex;flex-wrap:wrap;justify-content:center;text-align:center}.xr{display:flex;flex-wrap:wrap;justify-content:flex-end;text-align:right}.xsb{display:flex;flex-wrap:wrap;justify-content:space-between}}@media (min-width: 0px){.yy{display:flex;flex-wrap:wrap;align-content:center}}@media (max-width: 768px){.sm\-xr{display:flex;flex-wrap:wrap;justify-content:flex-end;text-align:right}.sm\-xsb{display:flex;flex-wrap:wrap;justify-content:space-between}}:root{--x-columns: 12;--y-columns: 12;--y-height: 100%;--x-width: 100%}@media (min-width: 0px){.xvw{--x-width: 100vw}.x8{width:calc((var(--x-width) / var(--x-columns)) * 8) !important}.x12{width:calc((var(--x-width) / var(--x-columns)) * 12) !important}.yvh{--y-height: 100vh}.y12{height:calc((var(--y-height) / var(--y-columns)) * 12) !important}}@media (max-width: 768px){.sm\-x4{width:calc((var(--x-width) / var(--x-columns)) * 4) !important}.sm\-x12{width:calc((var(--x-width) / var(--x-columns)) * 12) !important}}@media (max-width: 992px){.md\-x12{width:calc((var(--x-width) / var(--x-columns)) * 12) !important}}@media (min-width: 768px){.md\+x4{width:calc((var(--x-width) / var(--x-columns)) * 4) !important}.md\+x6{width:calc((var(--x-width) / var(--x-columns)) * 6) !important}.md\+x10{width:calc((var(--x-width) / var(--x-columns)) * 10) !important}}@media (max-width: 1200px){.lg\-x12z{max-width:calc((var(--x-width) / var(--x-columns)) * 12) !important}}@media (min-width: 992px){.lg\+x6{width:calc((var(--x-width) / var(--x-columns)) * 6) !important}}:root{--font-size: 0.25rem}@media (min-width: 0px){.fs4{font-size:calc(4 * var(--font-size))}.fs6{font-size:calc(6 * var(--font-size))}}@media (max-width: 768px){.sm\-fs6{font-size:calc(6 * var(--font-size))}.sm\-fs8{font-size:calc(8 * var(--font-size))}}@media (min-width: 768px){.md\+fs8{font-size:calc(8 * var(--font-size))}.md\+fs12{font-size:calc(12 * var(--font-size))}}:root{--primary-font-family: 'Arial';--secondary-font-family: 'Tahoma';--tertiary-font-family: 'Tahoma'}@media (min-width: 0px){.ff2{font-family:var(--secondary-font-family)}}@media (min-width: 0px){.fw3{font-weight:300}}:root{--line-height: 0.1}@media (min-width: 0px){.lh12{line-height:calc(12 * var(--line-height))}.lh16{line-height:calc(16 * var(--line-height))}}:root{--primary-color: #d22365;--primary-light-color: #ff94bd;--primary-dark-color: #530020;--secondary-color:#6b46c1;--secondary-light-color: #ceb9ff;--secondary-dark-color: #311e6d;--tertiary-color:#2ee9b4;--tertiary-light-color: #a4ffe5;--tertiary-dark-color:#004d37;--success-color: #75c38f;--success-light-color:#e5f7ec;--success-dark-color:#315542;--warning-color: #ffc400;--warning-light-color: #fbf6b4;--warning-dark-color: #663d1d;--danger-color:#ff3838;--danger-light-color: #f7e7e6;--danger-dark-color: #950000;--soft-color: #fff;--soft-light-color: transparent;--soft-dark-color: #e6e6e6;--neutral-color: #b3b3b3;--neutral-light-color: #e7e7e7;--neutral-dark-color: #999999;--heavy-color: #282c34;--heavy-light-color: #575f70;--heavy-dark-color:#0d0f11}@media (min-width: 0px){.bci1{border-color:var(--primary-color);border-width:1px;border-style:solid}.bcm2{border-color:var(--neutral-color);border-width:1px;border-style:solid}.bcm2t{border-top-color:var(--neutral-color);border-top-width:1px;border-top-style:solid}.bgi1{background-color:var(--primary-color)}.bgs2{background-color:var(--warning-color)}.bgm1{background-color:var(--soft-color)}.bgm2{background-color:var(--neutral-color)}.bgm3{background-color:var(--heavy-color)}.ci1{color:var(--primary-color)}.cm1{color:var(--soft-color)}.cm2z{color:var(--neutral-dark-color)}.cm3{color:var(--heavy-color)}.bgm1\:h:hover{background-color:var(--soft-color)}.ci1\:h:hover{color:var(--primary-color)}}@media (max-width: 768px){.sm\-bcm2t{border-top-color:var(--neutral-color);border-top-width:1px;border-top-style:solid}.sm\-bcm2b{border-bottom-color:var(--neutral-color);border-bottom-width:1px;border-bottom-style:solid}}@media (min-width: 768px){.md\+bci1{border-color:var(--primary-color);border-width:1px;border-style:solid}.md\+bcm2t,.md\+bcm2y{border-top-color:var(--neutral-color);border-top-width:1px;border-top-style:solid}.md\+bcm2y{border-bottom-color:var(--neutral-color);border-bottom-width:1px;border-bottom-style:solid}.md\+bgi1\:h:hover{background-color:var(--primary-color)}.md\+cm1\:h:hover{color:var(--soft-color)}}:root{--border-radius: 0.25rem}:root{--opacity: 0.05}@media (min-width: 0px){.op18{opacity:calc(18 * var(--opacity))}}:root{--letter-spacing: 0.1px}@media (min-width: 0px){.ls12{letter-spacing:calc(12 * var(--letter-spacing))}} - -@font-face{font-family:'Roboto';font-style:normal;font-weight:300;src:local(""),url("/roboto-v20-latin-300.woff2") format("woff2");font-display:swap}@font-face{font-family:'Roboto';font-style:normal;font-weight:500;src:local(""),url("/roboto-v20-latin-500.woff2") format("woff2");font-display:swap}@font-face{font-family:'Crete Round';font-style:normal;font-weight:400;src:local(""),url("/crete-round-v9-latin-regular.woff2") format("woff2");font-display:swap}:root{--primary-font-family: 'Crete Round', serif;--secondary-font-family: 'Roboto', sans-serif;--primary-color: #d22365;--soft-color: #fff;--neutral-color: #f0f0f2;--heavy-color: #282c34;--warning-color: #ffffd6}body{font-family:var(--secondary-font-family);font-weight:300;color:var(--heavy-color);padding-top:70px;overflow-y:scroll}h1,h2,h3,h4{font-family:var(--primary-font-family);font-weight:700;letter-spacing:1px}strong{font-weight:700}blockquote{line-height:140%}section a img{transition:.1s}section a img:hover{transform:scale(1.1);z-index:1;transition:.3s}pre{color:#ddbc72;font-size:1.2rem;width:100%;background-color:#282c34;line-height:1.5;padding:0.5rem;color:#ddbc72}@media (max-width: 768px){pre{overflow-x:auto}}.token.selector,.token.tag{color:#ff5d9a !important}.token.comment{color:#9dbcf7 !important}.token.string,.token.attr-value{color:#99c47a !important}.token.atrule,.token.keyword{color:#e89bff !important}.token.property,.token.boolean,.token.number,.token.constant,.token.symbol,.token.attr-name,.token.deleted{color:#e9ac72 !important}.token.function,.token.operator{color:#71bfff !important}.token.punctuation{color:#b1b8c6 !important}article :target::before{content:'';display:block;height:calc(80px + 1rem);margin-top:calc(-80px + -1rem)}article h2{margin-top:2rem}article p,article h2,article h3,article h4,article pre,article blockquote,article ul{margin-bottom:1rem}article ul{list-style-type:circle;padding-left:1.3rem}article li{padding:0.35rem 0}article a{color:var(--primary-color)}article h2 a,article h3 a{color:var(--heavy-color)}article em{font-weight:500;font-style:normal}article blockquote{border:1px solid var(--neutral-color);padding:0.5rem;margin-left:0}article blockquote p{margin-bottom:0}@media (max-width: 768px){article pre code.language-jsx{overflow-x:scroll}} - diff --git a/docs/client-f0cee0769c64977a287dddeaae84c7f6.js b/docs/client-f0cee0769c64977a287dddeaae84c7f6.js deleted file mode 100644 index 0231a68b..00000000 --- a/docs/client-f0cee0769c64977a287dddeaae84c7f6.js +++ /dev/null @@ -1 +0,0 @@ -!function(e){var t={};function __webpack_require__(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,__webpack_require__),r.l=!0,r.exports}__webpack_require__.m=e,__webpack_require__.c=t,__webpack_require__.d=function(e,t,n){__webpack_require__.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},__webpack_require__.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},__webpack_require__.t=function(e,t){if(1&t&&(e=__webpack_require__(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(__webpack_require__.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)__webpack_require__.d(n,r,function(t){return e[t]}.bind(null,r));return n},__webpack_require__.n=function(e){var t=e&&e.__esModule?function getDefault(){return e.default}:function getModuleExports(){return e};return __webpack_require__.d(t,"a",t),t},__webpack_require__.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},__webpack_require__.p="",__webpack_require__(__webpack_require__.s=3)}([function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){"use strict";n.r(t);n(0);const r=/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*))(?:Z|(\+|-)([\d|:]*))?$/,i=/^\/Date\((d|-|.*)\)[\/|\\]$/;function dateParser(e,t){if("string"==typeof t){let e=r.exec(t);if(e)return new Date(t);if(e=i.exec(t),e){const t=e[1].split(/[-+,.]/);return new Date(t[0]?+t[0]:0-+t[1])}}return t}function deserialize(e){return JSON.parse(e,dateParser)}function element_element(e,t={},...n){return null===t&&(t={}),n=function flattenChildren(e){return e=[].concat.apply([],e).map(e=>null!=e&&e),[].concat.apply([],e)}(n),"textarea"===e&&(n=[n.join("")]),"element"===e&&(e=t.tag||"div",delete t.tag),t.children=n,"function"==typeof e&&void 0!==e.render?{type:e,attributes:t,children:null}:{type:e,attributes:t,children:n}}function extractParamValue(e){return"true"===e||"false"!==e&&(e?decodeURIComponent(e.replace(/\+/g," ")):"")}function serializeParam(e){return e&&void 0!==e.toJSON?e.toJSON():e}function serializeSearch(e){return Object.keys(e).map(t=>!1===e[t]||e[t]?`${t}=${e[t]}`:"").filter(e=>!!e).join("&")}const s={};var a=s;const l={set(e,t,n){const r=serializeParam(n);e[t]=r;const i=serializeSearch(e);return N.url=N.path+(i?"?":"")+i,!0},get:(e,t)=>!1!==e[t]&&(!1!==a[t]&&(e[t]||a[t]||""))},o={...window.params};delete window.params;const c=new Proxy(o,l);function updateParams(e){!function resetSegments(){for(const e in s)delete s[e]}();const t=function getQueryStringParams(e){const[t,n]=e.split("?");return n?n.split("&").reduce((e,t)=>{let[n,r]=t.split("=");return e[n]=extractParamValue(r),e},{}):{}}(e);for(const e of Object.keys({...t,...o}))o[e]=t[e];return c}var u=c;const d={...window.environment,client:!0,server:!1};delete window.environment,Object.freeze(d);var m=d;function extractLocation(e){let[t,n]=e.split("#"),[r,i]=t.split("?");"/"!==r&&r.endsWith("/")&&(r=r.substring(0,r.length-1));let s=r;i&&(s+="?"+i);let a=s;return n&&(a+="#"+n),void 0===n&&(n=""),{path:r,search:i,url:s,urlWithHash:a,hash:n}}function isFalse(e){return null==e||!1===e||!1===e.type}function isClass(e){return"function"==typeof e.type&&"function"==typeof e.type.prototype.render}function isFunction(e){return"function"==typeof e.type&&void 0===e.type.prototype}function isStatic(e){return"function"==typeof e.type&&("function"==typeof e.type.render||e.type.name&&!e.type.prototype)}function isText(e){return void 0===e.children}const p=deserialize(JSON.stringify(window.context));delete window.context;const h={set(e,t,n){return p[t]=n,y.update(),Reflect.set(...arguments)},get:(e,t)=>void 0===e[t]?p[t]:e[t]};function generateContext(e){return new Proxy(e,h)}var f=p;function generateKey(e){return"_."+e.join(".")}function routableNode(e,t){if(function isRoutable(e){return e&&void 0!==e.attributes&&void 0!==e.attributes.route}(e)){const n=t.slice(0,-1).join(".");if(void 0!==y.routes[n])e.type=!1,e.children=[];else{const t=function routeMatches(e,t){let{path:n}=extractLocation(e);const r=n.split("/"),i=t.split("/"),s={},a=Math.max(r.length,i.length);let l=!1;for(let e=0;e{const{event:s,value:a}=n;"checked"==r?t[e.attributes.bind]=s.target[r]:!0===t[e.attributes.bind]||!1===t[e.attributes.bind]?t[e.attributes.bind]=s?"true"==s.target[r]:a:"number"==typeof t[e.attributes.bind]?t[e.attributes.bind]=parseFloat(s?s.target[r]:a)||0:t[e.attributes.bind]=s?s.target[r]:a,y.update(),void 0!==i&&setTimeout(()=>{i({...e.attributes,...n})},0)}}}function anchorableNode(e){if(function isAnchorable(e){return"a"===e.type&&e.attributes.href&&e.attributes.href.startsWith("/")&&!e.attributes.target}(e)){const t=e.attributes.onclick;e.attributes.onclick=({event:n})=>{n.preventDefault(),N.url=e.attributes.href,t&&setTimeout(()=>{t({...e.attributes,event:n})},0)}}}function anchorableElement(e){const t=e.querySelectorAll('a[href^="/"]:not([target])');for(const e of t)e.onclick=t=>{t.preventDefault(),N.url=e.getAttribute("href")}}function parameterizableNode_anchorableNode(e,t,n){if(function isParameterizable(e){return e&&e.attributes&&(e.attributes.params||e.attributes.path)}(e)){let r;if(e.attributes.params){r={};for(const t in e.attributes.params)r[t]=serializeParam(e.attributes.params[t])}else r=n;const i=serializeSearch(r),s=e.attributes.path||t.path;e.attributes.href=s+(i?"?":"")+i,delete e.attributes.path,delete e.attributes.params}}function camelize(e){return e.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g,(e,t)=>t.toUpperCase())}function kebabize(e){return e.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g,"$1-$2").toLowerCase()}function datableNode(e){if(e&&e.attributes){e.attributes.data=e.attributes.data||{};for(const t in e.attributes)if(t.startsWith("data-")){const n=camelize(t.slice(5));e.attributes.data[n]=e.attributes[t]}for(const t in e.attributes.data){const n="data-"+kebabize(t);e.attributes[n]=e.attributes.data[t]}}}function objectEvent(e){for(const t in e.attributes)if(t.startsWith("on")&&"object"==typeof e.attributes[t]){const n=e.attributes.source,r=e.attributes[t];e.attributes[t]=function(){for(const e in r)n[e]=r[e];y.update()}.bind(n)}}function render(e,t){if(routableNode(e,t),datableNode(e),isFalse(e)||"head"===e.type)return document.createComment("");if(objectEvent(e),bindableNode(e),isStatic(e)){const n=(e.type.render||e.type).call(e.type,{...f,...e.attributes});return e.children=[n],render(e.children[0],[...t,0])}if(isFunction(e)){const n=e.type(e.attributes);return e.children=[n],render(e.children[0],[...t,0])}if(isClass(e)){const n=e.attributes.key||generateKey([0,...t]),r=y.instances[n]||new e.type,i=window.instances[n];if(i){for(const e in i)r[e]=i[e];delete window.instances[n],r._self.initiated=!0,r._self.prerendered=!0}r._events={},r._attributes=e.attributes;const s=generateContext(e.attributes);r._context=s,i||null!=y.instances[n]||(y.initiationQueue.push(r),r.prepare&&r.prepare()),y.instances[n]=r;const a=r.render();return e.children=[a],y.renewalQueue.push(r),r._self.element=render(e.children[0],[...t,0]),y.hydrationQueue.push(r),r._self.element}if(isText(e))return document.createTextNode(e);let n,r=y.nextVirtualDom,i=!1;for(const e of t){if(!r.children)break;if(r=r.children[e],!r)break;if("svg"===r.type){i=!0;break}}n=i?document.createElementNS("http://www.w3.org/2000/svg",e.type):document.createElement(e.type),parameterizableNode_anchorableNode(e,N,u),anchorableNode(e);for(let r in e.attributes)if("html"===r)n.innerHTML=e.attributes[r],anchorableElement(n);else if(r.startsWith("on")){const i=r.replace("on",""),s=generateKey(t)+"."+i;y.events[s]=t=>{!0!==e.attributes.default&&t.preventDefault(),e.attributes[r]({...e.attributes,event:t})},n.addEventListener(i,y.events[s])}else{const t=typeof e.attributes[r];"object"!==t&&"function"!==t&&("value"!=r&&!0===e.attributes[r]?n.setAttribute(r,""):("value"==r||!1!==e.attributes[r]&&null!==e.attributes[r]&&void 0!==e.attributes[r])&&n.setAttribute(r,e.attributes[r]))}if(!e.attributes.html){for(let r=0;rA curated list of Nullstack components made by the community.
If you want to add a component to this list open an issue on github .
The data is an object in the framework store part of your context and gives you information about the element dataset.
-You can use this key to avoid polluting your DOM with invalid attributes.
---💡 This helps Nullstack set attributes without wasting time validating them.
-
This key is readonly and available only in the client context.
-Any data-* attributes will receive a respective camelized key on the data object.
-You can assign data attributes both via data-* and a data key that accepts an object with camelized keys.
-The kebab version is also available in the context.
-import Nullstack from 'nullstack';
-
-class ContextData extends Nullstack {
-
- count = 1;
-
- calculate({data}) {
- this.count = this.count * data.multiply + data.sum;
- }
-
- renderInner(context) {
- const {data} = context;
- return (
- <div data={data}>
- {data.frameworkName}
- is same as
- {context['data-framework-name']}
- </div>
- )
- }
-
- render({data}) {
- return (
- <div>
- <button onclick={this.calculate} data-multiply={3} data={{sum: 2}}>
- Calculate
- </button>
- <Inner data-framework-name="Nullstack" />
- </div>
- )
- }
-
-}
-
-export default ContextData;
-
---💡 Camelized keys from the data object will result in kebab attributes in the DOM.
-
⚔ Learn about the context environment.
-The data is an object in the framework store part of your context and gives you information about the element dataset.
\nYou can use this key to avoid polluting your DOM with invalid attributes.
\n\n\n💡 This helps Nullstack set attributes without wasting time validating them.
\n
This key is readonly and available only in the client context.
\nAny data-* attributes will receive a respective camelized key on the data object.
\nYou can assign data attributes both via data-* and a data key that accepts an object with camelized keys.
\nThe kebab version is also available in the context.
\nimport Nullstack from 'nullstack';\n\nclass ContextData extends Nullstack {\n\n count = 1;\n\n calculate({data}) {\n this.count = this.count * data.multiply + data.sum;\n }\n\n renderInner(context) {\n const {data} = context;\n return (\n <div data={data}>\n {data.frameworkName}\n is same as\n {context['data-framework-name']}\n </div>\n )\n }\n \n render({data}) {\n return (\n <div> \n <button onclick={this.calculate} data-multiply={3} data={{sum: 2}}>\n Calculate\n </button>\n <Inner data-framework-name=\"Nullstack\" />\n </div>\n )\n }\n\n}\n\nexport default ContextData;\n\n\n\n💡 Camelized keys from the data object will result in kebab attributes in the DOM.
\n
⚔ Learn about the context environment.
\n","description":"The data is an object in the framework store part of your context and gives you information about the element dataset."},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Context Data - Nullstack","description":"The data is an object in the framework store part of your context and gives you information about the element dataset."}} \ No newline at end of file diff --git a/docs/context-environment/index.html b/docs/context-environment/index.html deleted file mode 100644 index 0f3a7f8d..00000000 --- a/docs/context-environment/index.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - -The environment object is in the framework store part of your context and gives you information about the current environment.
-This key is readonly and available in both the client and server contexts.
-The following keys are available in the object:
-import Nullstack from 'nullstack';
-
-class Page extends Nullstack {
-
- render({environment}) {
- return (
- <div>
- {environment.client && <p> I am in the client </p>}
- {environment.server && <p> I am in the server </p>}
- {environment.development && <p> I am in development mode </p>}
- {environment.production && <p> I am in production mode </p>}
- {environment.static && <p> I am a static site </p>}
- <p> My key is {environment.key} </p>
- </div>
- )
- }
-
-}
-
-export default Page;
-
-The environment key is an md5 hash of the environment folder outputs that is appended to assets and static API path in order to assist cache control.
-⚔ Learn about the context page.
-The environment object is in the framework store part of your context and gives you information about the current environment.
\nThis key is readonly and available in both the client and server contexts.
\nThe following keys are available in the object:
\nimport Nullstack from 'nullstack';\n\nclass Page extends Nullstack {\n \n render({environment}) {\n return (\n <div> \n {environment.client && <p> I am in the client </p>}\n {environment.server && <p> I am in the server </p>}\n {environment.development && <p> I am in development mode </p>}\n {environment.production && <p> I am in production mode </p>}\n {environment.static && <p> I am a static site </p>}\n <p> My key is {environment.key} </p>\n </div>\n )\n }\n\n}\n\nexport default Page;\n\nThe environment key is an md5 hash of the environment folder outputs that is appended to assets and static API path in order to assist cache control.
\n⚔ Learn about the context page.
\n","description":"The environment object is in the framework store part of your context and gives you information about the current environment"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Context Environment - Nullstack","description":"The environment object is in the framework store part of your context and gives you information about the current environment"}} \ No newline at end of file diff --git a/docs/context-page/index.html b/docs/context-page/index.html deleted file mode 100644 index 64e7204f..00000000 --- a/docs/context-page/index.html +++ /dev/null @@ -1,159 +0,0 @@ - - - - - - -The page object is a proxy in the framework store part of your context and gives you information about the document head metatags.
-This key is readwrite and available only in the client context.
-Page keys will be used to generate metatags during server-side rendering and must be assigned before initiate is resolved.
-The following keys are available in the object:
-When the title key is assigned on the client-side, the document title will be updated.
-Nullstack uses the changes and priority keys to generate the sitemap.xml.
-The sitemap is generated automatically only when using static site generation and must be manually generated in server-side rendered applications
-The changes key represents the changefreq key in the sitemap.xml and if assigned must be one of the following values:
-The priority key is a number between 0.0 and 1.0 that represents the priority key in the sitemap.xml.
-Nullstack does not set a default priority, however, sitemaps assume a 0.5 priority when not explicitly set.
-Besides title and locale all other keys have sensible defaults generated based on the application scope.
-import Nullstack from 'nullstack';
-
-class Page extends Nullstack {
-
- prepare({project, page}) {
- page.title = `${project.name} - Page Title`;
- page.image = '/image.jpg';
- page.description = 'Page meta description';
- page.canonical = 'http://absolute.url/canonical-link';
- page.locale = 'pt-BR';
- page.robots = 'index, follow';
- page.schema = {};
- page.changes = 'weekly';
- page.priority = 1;
- }
-
- render({page}) {
- return (
- <div>
- <h1> {page.title} </h1>
- <p> {page.description} </p>
- </div>
- )
- }
-
-}
-
-export default Page;
-
-Updating page.title will raise a custom event.
-import Nullstack from 'nullstack';
-
-class Analytics extends Nullstack {
-
- hydrate({page}) {
- window.addEventListener(page.event, () => {
- console.log(page.title);
- });
- }
-
-}
-
-export default Analytics;
-
-If during the server-side render process the page.status has any value besides 200, your application will receive another render pass that gives you the chance to adjust the interface according to the status.
-The status key will be raised with the HTTP response.
-The page status will be modified to 500 and receive another render pass if the page raise an exception while rendering.
-The status of server functions responses will be set to the page.status.
-import Nullstack from 'nullstack';
-import ErrorPage from './ErrorPage';
-import HomePage from './HomePage';
-
-class Application extends Nullstack {
-
- // ...
-
- render({page}) {
- return (
- <main>
- {page.status !== 200 && <ErrorPage route="*" />}
- <HomePage route="/" />
- </main>
- )
- }
-
-}
-
-export default Application;
-
---🔥 Assigning to the status key during the single-page application mode will have no effect.
-
⚔ Learn about the context project.
-The page object is a proxy in the framework store part of your context and gives you information about the document head metatags.
\nThis key is readwrite and available only in the client context.
\nPage keys will be used to generate metatags during server-side rendering and must be assigned before initiate is resolved.
\nThe following keys are available in the object:
\nWhen the title key is assigned on the client-side, the document title will be updated.
\nNullstack uses the changes and priority keys to generate the sitemap.xml.
\nThe sitemap is generated automatically only when using static site generation and must be manually generated in server-side rendered applications
\nThe changes key represents the changefreq key in the sitemap.xml and if assigned must be one of the following values:
\nThe priority key is a number between 0.0 and 1.0 that represents the priority key in the sitemap.xml.
\nNullstack does not set a default priority, however, sitemaps assume a 0.5 priority when not explicitly set.
\nBesides title and locale all other keys have sensible defaults generated based on the application scope.
\nimport Nullstack from 'nullstack';\n\nclass Page extends Nullstack {\n\n prepare({project, page}) {\n page.title = `${project.name} - Page Title`;\n page.image = '/image.jpg';\n page.description = 'Page meta description';\n page.canonical = 'http://absolute.url/canonical-link';\n page.locale = 'pt-BR';\n page.robots = 'index, follow';\n page.schema = {};\n page.changes = 'weekly';\n page.priority = 1;\n }\n\n render({page}) {\n return (\n <div>\n <h1> {page.title} </h1>\n <p> {page.description} </p>\n </div>\n )\n }\n\n}\n\nexport default Page;\n\nUpdating page.title will raise a custom event.
\nimport Nullstack from 'nullstack';\n\nclass Analytics extends Nullstack {\n\n hydrate({page}) {\n window.addEventListener(page.event, () => {\n console.log(page.title);\n });\n }\n\n}\n\nexport default Analytics;\n\nIf during the server-side render process the page.status has any value besides 200, your application will receive another render pass that gives you the chance to adjust the interface according to the status.
\nThe status key will be raised with the HTTP response.
\nThe page status will be modified to 500 and receive another render pass if the page raise an exception while rendering.
\nThe status of server functions responses will be set to the page.status.
\nimport Nullstack from 'nullstack';\nimport ErrorPage from './ErrorPage';\nimport HomePage from './HomePage';\n\nclass Application extends Nullstack {\n\n // ...\n\n render({page}) {\n return (\n <main>\n {page.status !== 200 && <ErrorPage route=\"*\" />}\n <HomePage route=\"/\" />\n </main>\n )\n }\n\n}\n\nexport default Application;\n\n\n\n🔥 Assigning to the status key during the single-page application mode will have no effect.
\n
⚔ Learn about the context project.
\n","description":"The page object is a proxy in the framework store part of your context and gives you information about the document head metatags"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Context Page - Nullstack","description":"The page object is a proxy in the framework store part of your context and gives you information about the document head metatags"}} \ No newline at end of file diff --git a/docs/context-project/index.html b/docs/context-project/index.html deleted file mode 100644 index 01af05cf..00000000 --- a/docs/context-project/index.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - -The project object is a proxy in the framework store part of your context and gives you information about the app manifest and some metatags.
-This key is readwrite in the server context.
-This key is readonly in the client context.
-Project keys will be used to generate metatags during server-side rendering and must be assigned before initiate is resolved.
-Project keys will be used to generate the app manifest and should be set during the application startup.
-The disallow key will be used to generate the robots.txt and should be set during the application startup.
-Project keys are frozen after the application startup.
-The following keys are available in the object:
-Besides domain, name and color all other keys have sensible defaults generated based on the application scope.
-If you do not declare the icons key, Nullstack will scan any icons with the name following the pattern "icon-[WIDTH]x[HEIGHT].png" in your public folder.
-If the sitemap key is set to true your robots.txt file will point the sitemap to https://${project.domain}/sitemap.xml.
-The cdn key will prefix your asset bundles and will be available in the context so you can manually prefix other assets.
-The protocol key is "http" in development mode and "https" in production mode by default
-import Nullstack from 'nullstack';
-
-class Application extends Nullstack {
-
- static async start({project}) {
- project.name = 'Nullstack';
- project.shortName = 'Nullstack';
- project.domain = 'nullstack.app';
- project.color = '#d22365';
- project.backgroundColor = '#d22365';
- project.type = 'website';
- project.display = 'standalone';
- project.orientation = 'portrait';
- project.scope = '/';
- project.root = '/';
- project.icons = {
- '72': '/icon-72x72.png',
- '128': '/icon-128x128.png',
- '512': '/icon-512x512.png'
- };
- project.favicon = '/favicon.png';
- project.disallow = ['/admin'];
- project.sitemap = true;
- project.cdn = 'cdn.nullstack.app';
- project.protocol = 'https';
- }
-
- prepare({project, page}) {
- page.title = project.name;
- }
-
-}
-
-export default Application;
-
---💡 You can override the automatically generated manifest.json and robots.txt by serving your own file from the public folder
-
⚔ Learn about the context settings.
-The project object is a proxy in the framework store part of your context and gives you information about the app manifest and some metatags.
\nThis key is readwrite in the server context.
\nThis key is readonly in the client context.
\nProject keys will be used to generate metatags during server-side rendering and must be assigned before initiate is resolved.
\nProject keys will be used to generate the app manifest and should be set during the application startup.
\nThe disallow key will be used to generate the robots.txt and should be set during the application startup.
\nProject keys are frozen after the application startup.
\nThe following keys are available in the object:
\nBesides domain, name and color all other keys have sensible defaults generated based on the application scope.
\nIf you do not declare the icons key, Nullstack will scan any icons with the name following the pattern "icon-[WIDTH]x[HEIGHT].png" in your public folder.
\nIf the sitemap key is set to true your robots.txt file will point the sitemap to https://${project.domain}/sitemap.xml.
\nThe cdn key will prefix your asset bundles and will be available in the context so you can manually prefix other assets.
\nThe protocol key is "http" in development mode and "https" in production mode by default
\nimport Nullstack from 'nullstack';\n\nclass Application extends Nullstack {\n\n static async start({project}) {\n project.name = 'Nullstack';\n project.shortName = 'Nullstack';\n project.domain = 'nullstack.app';\n project.color = '#d22365';\n project.backgroundColor = '#d22365';\n project.type = 'website';\n project.display = 'standalone';\n project.orientation = 'portrait';\n project.scope = '/';\n project.root = '/';\n project.icons = {\n '72': '/icon-72x72.png',\n '128': '/icon-128x128.png',\n '512': '/icon-512x512.png'\n };\n project.favicon = '/favicon.png';\n project.disallow = ['/admin'];\n project.sitemap = true;\n project.cdn = 'cdn.nullstack.app';\n project.protocol = 'https';\n }\n\n prepare({project, page}) {\n page.title = project.name;\n }\n\n}\n\nexport default Application;\n\n\n\n💡 You can override the automatically generated manifest.json and robots.txt by serving your own file from the public folder
\n
⚔ Learn about the context settings.
\n","description":"The project object is a proxy in the framework store part of your context and gives you information about the app manifest and some metatags"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Context Project - Nullstack","description":"The project object is a proxy in the framework store part of your context and gives you information about the app manifest and some metatags"}} \ No newline at end of file diff --git a/docs/context-secrets/index.html b/docs/context-secrets/index.html deleted file mode 100644 index 9bc57550..00000000 --- a/docs/context-secrets/index.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - -The secrets object is a proxy in the framework store part of your context which you can use to configure your application with private information.
-This key is readwrite and available only in the server context.
-Secrets keys are frozen after the application startup.
-The following keys are available in the object:
-You can assign keys to development or production keys in order to have different secrets per environment.
-If you assign a key directly to the secrets object it will be available in both environments.
-When reading from a key you must read directly from the secrets object and Nullstack will return the best-suited value for that environment.
-import Nullstack from 'nullstack';
-
-class Application extends Nullstack {
-
- static async start({secrets}) {
- secrets.development.privateKey = 'SANDBOX_API_KEY';
- secrets.production.privateKey = 'PRODUCTION_API_KEY';
- secrets.endpoint = 'https://domain.com/api';
- }
-
- static async fetchFromApi({secrets}) {
- const response = await fetch(secrets.endpoint, {
- headers: {
- Authorization: `Bearer ${secrets.privateKey}`
- }
- });
- return await response.json();
- }
-
-}
-
-export default Application;
-
-Any environment key starting with NULLSTACK_SECRETS_ will be mapped to the secrets in that environment.
---🐱💻 NULLSTACK_SECRETS_PRIVATE_KEY will be mapped to secrets.privateKey
-
⚔ Learn about the instance self.
-The secrets object is a proxy in the framework store part of your context which you can use to configure your application with private information.
\nThis key is readwrite and available only in the server context.
\nSecrets keys are frozen after the application startup.
\nThe following keys are available in the object:
\nYou can assign keys to development or production keys in order to have different secrets per environment.
\nIf you assign a key directly to the secrets object it will be available in both environments.
\nWhen reading from a key you must read directly from the secrets object and Nullstack will return the best-suited value for that environment.
\nimport Nullstack from 'nullstack';\n\nclass Application extends Nullstack {\n\n static async start({secrets}) {\n secrets.development.privateKey = 'SANDBOX_API_KEY';\n secrets.production.privateKey = 'PRODUCTION_API_KEY';\n secrets.endpoint = 'https://domain.com/api';\n }\n\n static async fetchFromApi({secrets}) {\n const response = await fetch(secrets.endpoint, {\n headers: {\n Authorization: `Bearer ${secrets.privateKey}`\n }\n });\n return await response.json();\n }\n\n}\n\nexport default Application;\n\nAny environment key starting with NULLSTACK_SECRETS_ will be mapped to the secrets in that environment.
\n\n\n🐱💻 NULLSTACK_SECRETS_PRIVATE_KEY will be mapped to secrets.privateKey
\n
⚔ Learn about the instance self.
\n","description":"The secrets object is a proxy in the framework store part of your context which you can use to configure your application with private information"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Context Secrets - Nullstack","description":"The secrets object is a proxy in the framework store part of your context which you can use to configure your application with private information"}} \ No newline at end of file diff --git a/docs/context-settings/index.html b/docs/context-settings/index.html deleted file mode 100644 index eb0ab4b7..00000000 --- a/docs/context-settings/index.html +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - -The settings object is a proxy in the framework store part of your context which you can use to configure your application with public information.
-This key is readwrite in the server context.
-This key is readonly in the client context.
-Settings keys are frozen after the application startup.
-The following keys are available in the object:
-You can assign keys to development or production keys in order to have different settings per environment.
-If you assign a key directly to the settings object it will be available in both environments.
-When reading from a key you must read directly from the settings object and Nullstack will return the best-suited value for that environment.
-import Nullstack from 'nullstack';
-
-class Application extends Nullstack {
-
- static async start({settings}) {
- settings.development.publicKey = 'SANDBOX_API_KEY';
- settings.production.publicKey = 'PRODUCTION_API_KEY';
- settings.endpoint = 'https://domain.com/api';
- }
-
- async hydrate({settings}) {
- const response = await fetch(settings.endpoint, {
- headers: {
- Authorization: `Bearer ${settings.publicKey}`
- }
- });
- this.data = await response.json();
- }
-
-}
-
-export default Application;
-
-Any environment key starting with NULLSTACK_SETTINGS_ will be mapped to the settings in that environment.
---🐱💻 NULLSTACK_SETTINGS_PUBLIC_KEY will be mapped to settings.publicKey
-
⚔ Learn about the context secrets.
-The settings object is a proxy in the framework store part of your context which you can use to configure your application with public information.
\nThis key is readwrite in the server context.
\nThis key is readonly in the client context.
\nSettings keys are frozen after the application startup.
\nThe following keys are available in the object:
\nYou can assign keys to development or production keys in order to have different settings per environment.
\nIf you assign a key directly to the settings object it will be available in both environments.
\nWhen reading from a key you must read directly from the settings object and Nullstack will return the best-suited value for that environment.
\nimport Nullstack from 'nullstack';\n\nclass Application extends Nullstack {\n\n static async start({settings}) {\n settings.development.publicKey = 'SANDBOX_API_KEY';\n settings.production.publicKey = 'PRODUCTION_API_KEY';\n settings.endpoint = 'https://domain.com/api';\n }\n\n async hydrate({settings}) {\n const response = await fetch(settings.endpoint, {\n headers: {\n Authorization: `Bearer ${settings.publicKey}`\n }\n });\n this.data = await response.json();\n }\n\n}\n\nexport default Application;\n\nAny environment key starting with NULLSTACK_SETTINGS_ will be mapped to the settings in that environment.
\n\n\n🐱💻 NULLSTACK_SETTINGS_PUBLIC_KEY will be mapped to settings.publicKey
\n
⚔ Learn about the context secrets.
\n","description":"The settings object is a proxy in the framework store part of your context which you can use to configure your application with public information"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Context Settings - Nullstack","description":"The settings object is a proxy in the framework store part of your context which you can use to configure your application with public information"}} \ No newline at end of file diff --git a/docs/context/index.html b/docs/context/index.html deleted file mode 100644 index 4ca7d7ef..00000000 --- a/docs/context/index.html +++ /dev/null @@ -1,198 +0,0 @@ - - - - - - -Every function in Nullstack receives a context as the argument.
-There are two contexts, one for the client and another one for the server.
-The client context lives as long as the browser tab is open.
-The server context lives as long as the server is running.
-Both contexts are proxies that merge the keys of 3 objects:
-These are the information that the framework makes available to you by default.
-When you set a key to the context it will be available for destructuring at any depth of the application, even the parents of your component or 3rd party applications that mount your component.
-Updating a key in the context causes the application to re-render automatically.
-You can think of this as a single concept to replace stores, contexts, services, and reducers at the same time using the dependency injection pattern with vanilla javascript objects instead.
-import Nullstack from 'nullstack';
-
-class Counter extends Nullstack {
-
- prepare(context) {
- context.count = 1;
- }
-
- static async updateTotalCount(context) {
- context.totalCount += count;
- }
-
- async double(context) {
- context.count += context.count;
- await this.updateTotalCount({count: context.count});
- }
-
- render({count}) {
- return (
- <button onclick={this.double}> {count} </button>
- )
- }
-
-}
-
-export default Counter;
-
-import Nullstack from 'nullstack';
-import Counter from './Counter';
-
-class Application extends Nullstack {
-
- static async start(context) {
- context.totalCount = 0;
- }
-
- render({count}) {
- return (
- <main>
- {(!count || count < 10) && <Counter />}
- </main>
- )
- }
-
-}
-
-export default Application;
-
-These are the attributes you declare in your tag.
-If the attribute is declared in a component tag every function of that component will have access to that attribute in its context.
-If the attribute is declared in a tag that has an event it will be merged into the event function context.
-import Nullstack from 'nullstack';
-
-class Counter extends Nullstack {
-
- add(context) {
- context.count += context.delta + context.amount;
- }
-
- render({count, delta}) {
- return (
- <button onclick={this.add} amount={1}>
- add {delta} to {count}
- </button>
- )
- }
-
-}
-
-export default Counter;
-
-import Nullstack from 'nullstack';
-import Counter from './Counter';
-
-class Application extends Nullstack {
-
- prepare(context) {
- context.count = 0;
- }
-
- render() {
- return (
- <main>
- <Counter delta={2} />}
- </main>
- )
- }
-
-}
-
-export default Application;
-
-Every function of subclasses of Nullstack is injected with a copy of the instance context merged with its arguments.
-Arguments are optional, but if declared, must be a single object with keys of your choice.
-import Nullstack from 'nullstack';
-
-class Counter extends Nullstack {
-
- prepare() {
- this.add();
- this.add({amount: 2});
- }
-
- add(context) {
- context.count += context.amount || 1;
- }
-
-}
-
-export default Counter;
-
-⚔ Learn about routes and params.
-Every function in Nullstack receives a context as the argument.
\nThere are two contexts, one for the client and another one for the server.
\nThe client context lives as long as the browser tab is open.
\nThe server context lives as long as the server is running.
\nBoth contexts are proxies that merge the keys of 3 objects:
\nThese are the information that the framework makes available to you by default.
\nWhen you set a key to the context it will be available for destructuring at any depth of the application, even the parents of your component or 3rd party applications that mount your component.
\nUpdating a key in the context causes the application to re-render automatically.
\nYou can think of this as a single concept to replace stores, contexts, services, and reducers at the same time using the dependency injection pattern with vanilla javascript objects instead.
\nimport Nullstack from 'nullstack';\n\nclass Counter extends Nullstack {\n\n prepare(context) {\n context.count = 1;\n }\n\n static async updateTotalCount(context) {\n context.totalCount += count;\n }\n\n async double(context) {\n context.count += context.count;\n await this.updateTotalCount({count: context.count});\n }\n \n render({count}) {\n return (\n <button onclick={this.double}> {count} </button>\n )\n }\n\n}\n\nexport default Counter;\n\nimport Nullstack from 'nullstack';\nimport Counter from './Counter';\n\nclass Application extends Nullstack {\n\n static async start(context) {\n context.totalCount = 0;\n }\n \n render({count}) {\n return (\n <main>\n {(!count || count < 10) && <Counter />}\n </main>\n )\n }\n\n}\n\nexport default Application;\n\nThese are the attributes you declare in your tag.
\nIf the attribute is declared in a component tag every function of that component will have access to that attribute in its context.
\nIf the attribute is declared in a tag that has an event it will be merged into the event function context.
\nimport Nullstack from 'nullstack';\n\nclass Counter extends Nullstack {\n\n add(context) {\n context.count += context.delta + context.amount;\n }\n \n render({count, delta}) {\n return (\n <button onclick={this.add} amount={1}> \n add {delta} to {count}\n </button>\n )\n }\n\n}\n\nexport default Counter;\n\nimport Nullstack from 'nullstack';\nimport Counter from './Counter';\n\nclass Application extends Nullstack {\n\n prepare(context) {\n context.count = 0;\n }\n \n render() {\n return (\n <main>\n <Counter delta={2} />}\n </main>\n )\n }\n\n}\n\nexport default Application;\n\nEvery function of subclasses of Nullstack is injected with a copy of the instance context merged with its arguments.
\nArguments are optional, but if declared, must be a single object with keys of your choice.
\nimport Nullstack from 'nullstack';\n\nclass Counter extends Nullstack {\n\n prepare() {\n this.add();\n this.add({amount: 2});\n }\n\n add(context) {\n context.count += context.amount || 1;\n }\n\n}\n\nexport default Counter;\n\n⚔ Learn about routes and params.
\n","description":"Create full-stack javascript applications within seconds"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Context - Nullstack","description":"Create full-stack javascript applications within seconds"}} \ No newline at end of file diff --git a/docs/contributors/index.html b/docs/contributors/index.html deleted file mode 100644 index 6e736307..00000000 --- a/docs/contributors/index.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - -Nullstack is being developed since January 2019 with features being extracted from freelancing projects.
At this point the API is stable, lifecycle methods and context keys will pretty much be frozen.
We are not yet on 1.0 but really close, the only thing missing is to test it on applications made outside the core team to figure out if it fits the needs of other programmers.
The next updates will be guided towards fixing any bugs that are found and focus on quality of life.
The following updates are the next planned steps in no particular order:
Nullstack was developed by full-stack neuro-atypical freelancers.
With a heavy background in Rails, Ember.js, and React.js, the inspirations took from those projects might be obvious.

Creator of the concept. Comes with new API proposals to its favorite rubber ducks and returns with commits.
Reverse engineered wishful thinking code into existence and then refactored it into a framework.

Rubber duck with human skills that makes sure the code is not going too far outside the box, then makes the box look nice.
API reviewer that developed third party projects to test proof of concepts from a front-end focused perspective.

Rubber duck with a neck to find inconsistencies and problems, waiting till an API is approved to force us into rewriting everything.
An early adopter of the framework that developed real production applications to validate how the parts fit together.
It's simple. Found a bug or want a new feature?
Create an issue or submit a pull request with tests.
Follow these steps and become a full-stack javascript developer!
Start your journey in Nullstack with these basic concepts
These are concepts that you will most likely learn as you need in your projects
The best way to learn Nullstack is by reading some code
Lifecycle methods are special named functions that you can declare in the class.
-Each lifecycle method runs in a specific order in a queue so it's guaranteed that all components initiated in that cycle will be prepared before the first one is initiated.
-This method is blocking and runs before the first time the component is rendered.
-You can use this function to set the state that the user will see before things are loaded.
-If the user is entering from this route prepare will run in the server before Nullstack server-side renders your application.
-If the user is navigating from another route this method will run in the client.
-import Nullstack from 'nullstack';
-
-class Component extends Nullstack {
-
- // ...
-
- prepare() {
- this.date = new Date();
- }
-
- // ...
-
-}
-
-export default Component;
-
-This method can be async and runs right after the component is prepared and rendered for the first time.
-You can use this function to invoke another server function and load the data to present the page.
-If the user is entering from this route initiate will run in the server.
-Nullstack will wait till the promise is resolved and then finally generate the HTML that will be served.
-If the user is navigating from another route this method will run in the client.
-import Nullstack from 'nullstack';
-
-class Component extends Nullstack {
-
- // ...
-
- async initiate() {
- this.task = await getTaskByDay({
- day: this.date
- });
- }
-
- // ...
-
-}
-
-export default Component;
-
---✨ Learn more about server functions.
-
This method is async and will only run in the client.
-This method will always run no matter which environment started the component.
-This is a good place to trigger dependencies that manipulate the dom or can only run on the client-side.
-import Nullstack from 'nullstack';
-
-class Component extends Nullstack {
-
- // ...
-
- async hydrate() {
- this.timer = setInterval(() => {
- console.log(this.date);
- }, 1000);
- }
-
- // ...
-
-}
-
-export default Component;
-
-This method is async and will only run in the client.
-This method runs on every component anytime the application state changes.
---🔥 Be careful not to cause infinite loopings when mutating state inside update.
-
This will run right before rendering but will not block the rendering queue.
-The update function will not start running until the application is rendered after the initiate.
-import Nullstack from 'nullstack';
-
-class Component extends Nullstack {
-
- // ...
-
- async update() {
- const today = new Date();
- if(today.getDay() != this.date.getDay()) {
- this.date = today;
- await this.initiate();
- }
- }
-
- // ...
-
-}
-
-export default Component;
-
---✨ Lifecycle methods have no special side effects, you can call them manually without causing problems.
-
This method is async and will only run in the client.
-This method will run after your component leaves the DOM.
-This is the place to clean up whatever you set up in the hydrate method.
-The instance will be garbage collected after the promise is resolved.
-import Nullstack from 'nullstack';
-
-class Component extends Nullstack {
-
- // ...
-
- async terminate() {
- clearInterval(this.timer);
- }
-
- // ...
-
-}
-
-export default Component;
-
-⚔ Learn about server functions.
-Lifecycle methods are special named functions that you can declare in the class.
\nEach lifecycle method runs in a specific order in a queue so it's guaranteed that all components initiated in that cycle will be prepared before the first one is initiated.
\nThis method is blocking and runs before the first time the component is rendered.
\nYou can use this function to set the state that the user will see before things are loaded.
\nIf the user is entering from this route prepare will run in the server before Nullstack server-side renders your application.
\nIf the user is navigating from another route this method will run in the client.
\nimport Nullstack from 'nullstack';\n\nclass Component extends Nullstack {\n\n // ...\n\n prepare() {\n this.date = new Date();\n }\n\n // ...\n\n}\n\nexport default Component;\n\nThis method can be async and runs right after the component is prepared and rendered for the first time.
\nYou can use this function to invoke another server function and load the data to present the page.
\nIf the user is entering from this route initiate will run in the server.
\nNullstack will wait till the promise is resolved and then finally generate the HTML that will be served.
\nIf the user is navigating from another route this method will run in the client.
\nimport Nullstack from 'nullstack';\n\nclass Component extends Nullstack {\n\n // ...\n\n async initiate() {\n this.task = await getTaskByDay({\n day: this.date\n });\n }\n\n // ...\n\n}\n\nexport default Component;\n\n\n\n✨ Learn more about server functions.
\n
This method is async and will only run in the client.
\nThis method will always run no matter which environment started the component.
\nThis is a good place to trigger dependencies that manipulate the dom or can only run on the client-side.
\nimport Nullstack from 'nullstack';\n\nclass Component extends Nullstack {\n\n // ...\n\n async hydrate() {\n this.timer = setInterval(() => {\n console.log(this.date);\n }, 1000);\n }\n\n // ...\n\n}\n\nexport default Component;\n\nThis method is async and will only run in the client.
\nThis method runs on every component anytime the application state changes.
\n\n\n🔥 Be careful not to cause infinite loopings when mutating state inside update.
\n
This will run right before rendering but will not block the rendering queue.
\nThe update function will not start running until the application is rendered after the initiate.
\nimport Nullstack from 'nullstack';\n\nclass Component extends Nullstack {\n\n // ...\n\n async update() {\n const today = new Date();\n if(today.getDay() != this.date.getDay()) {\n this.date = today;\n await this.initiate();\n }\n }\n\n // ...\n\n}\n\nexport default Component;\n\n\n\n✨ Lifecycle methods have no special side effects, you can call them manually without causing problems.
\n
This method is async and will only run in the client.
\nThis method will run after your component leaves the DOM.
\nThis is the place to clean up whatever you set up in the hydrate method.
\nThe instance will be garbage collected after the promise is resolved.
\nimport Nullstack from 'nullstack';\n\nclass Component extends Nullstack {\n\n // ...\n\n async terminate() {\n clearInterval(this.timer);\n }\n\n // ...\n\n}\n\nexport default Component;\n\n⚔ Learn about server functions.
\n","description":"Lifecycle methods are special named functions that you can declare in the class."},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Full-Stack Lifecycle - Nullstack","description":"Lifecycle methods are special named functions that you can declare in the class."}} \ No newline at end of file diff --git a/docs/getting-started/index.html b/docs/getting-started/index.html deleted file mode 100644 index d0161080..00000000 --- a/docs/getting-started/index.html +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - ---📌 You can watch a video tutorial on our Youtube Channel.
-
Create full-stack javascript applications within seconds using npx to generate your project files from the latest template.
---🔥 The minimum required node.js version for development mode is 12.12.0.
-
Replace project-name with your project name and run the command below to start a project:
-npx create-nullstack-app project-name
-
-Change directory to the generated folder:
-cd project-name
-
-Install the dependencies:
-npm install
-
-Start the application in development mode:
-npm start
-
-The following folders and files will be generated:
-This is the Webpack entry point.
-Usually, you don't have to touch this file, but it is a convenient place to import global dependencies like CSS frameworks.
-This folder will contain the actual source code of your application.
-This is your application main file.
---✨ Learn more about the njs file extension.
-
The start function will be automatically called once when you run npm start, use it to populate your server context with things like database, settings, and secrets.
---✨ Learn more about the application startup.
-
This is an empty file just to demonstrate that you can use SCSS with nullstack.
-It is a good practice to import a style file in a component with the same name.
---✨ Learn more about styles.
-
Every file in here will be available to anyone from the domain root.
-By default create-nullstack-app generates the icons required for your manifest.json and images for OG meta tags.
---✨ Learn more about manifest.json.
-
Be sure to replace these images with your project identity.
-This is the compiled result of your application in development mode.
---🔥 Do not touch this folder
-
This is the compiled result of your application in production mode.
---🔥 Do not touch this folder
-
--✨ Learn more about how to deploy a nullstack application.
-
⚔ Create your first renderable component.
-\n\n📌 You can watch a video tutorial on our Youtube Channel.
\n
Create full-stack javascript applications within seconds using npx to generate your project files from the latest template.
\n\n\n🔥 The minimum required node.js version for development mode is 12.12.0.
\n
Replace project-name with your project name and run the command below to start a project:
\nnpx create-nullstack-app project-name\n\nChange directory to the generated folder:
\ncd project-name\n\nInstall the dependencies:
\nnpm install\n\nStart the application in development mode:
\nnpm start\n\nThe following folders and files will be generated:
\nThis is the Webpack entry point.
\nUsually, you don't have to touch this file, but it is a convenient place to import global dependencies like CSS frameworks.
\nThis folder will contain the actual source code of your application.
\nThis is your application main file.
\n\n\n✨ Learn more about the njs file extension.
\n
The start function will be automatically called once when you run npm start, use it to populate your server context with things like database, settings, and secrets.
\n\n\n✨ Learn more about the application startup.
\n
This is an empty file just to demonstrate that you can use SCSS with nullstack.
\nIt is a good practice to import a style file in a component with the same name.
\n\n\n✨ Learn more about styles.
\n
Every file in here will be available to anyone from the domain root.
\nBy default create-nullstack-app generates the icons required for your manifest.json and images for OG meta tags.
\n\n\n✨ Learn more about manifest.json.
\n
Be sure to replace these images with your project identity.
\nThis is the compiled result of your application in development mode.
\n\n\n🔥 Do not touch this folder
\n
This is the compiled result of your application in production mode.
\n\n\n🔥 Do not touch this folder
\n
\n\n✨ Learn more about how to deploy a nullstack application.
\n
⚔ Create your first renderable component.
\n","description":"Create full-stack javascript applications within seconds"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Getting Started - Nullstack","description":"Create full-stack javascript applications within seconds"}} \ No newline at end of file diff --git a/docs/hero.svg b/docs/hero.svg deleted file mode 100644 index dc7dfffe..00000000 --- a/docs/hero.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/how-to-deploy-a-nullstack-application/index.html b/docs/how-to-deploy-a-nullstack-application/index.html deleted file mode 100644 index c69830f1..00000000 --- a/docs/how-to-deploy-a-nullstack-application/index.html +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - -With Nullstack it's easy to have your application up and running in production mode.
---🐱💻 stonks
-
Nullstack compiles your code and all your dependencies using Webpack.
-The output of the compilation is moved to the .production folder and is the only folder besides public that needs to be moved into the host machine.
-If you have project.cdn set you must move the public folder to the actual cdn.
---💡 It is important that the .production folder is present for environment detection
-
The host machine must have at least node v8.10.0 installed.
-You don't have to "npm install" in the host machine.
---✨ You can configure the environment using settings and secrets
-
To start the server just run:
-node .production/server.js
-
---✨ It is recommend the usage of a process manager like PM2
-
After you generate a static site, all you have to do is move the output folder to any host machine capable of serving HTML.
---🎉 Congratulations. You are done with the advanced concepts!
-
⚔ Learn how to use MongoDB with Nullstack.
-With Nullstack it's easy to have your application up and running in production mode.
\n\n\n🐱💻 stonks
\n
Nullstack compiles your code and all your dependencies using Webpack.
\nThe output of the compilation is moved to the .production folder and is the only folder besides public that needs to be moved into the host machine.
\nIf you have project.cdn set you must move the public folder to the actual cdn.
\n\n\n💡 It is important that the .production folder is present for environment detection
\n
The host machine must have at least node v8.10.0 installed.
\nYou don't have to "npm install" in the host machine.
\n\n\n✨ You can configure the environment using settings and secrets
\n
To start the server just run:
\nnode .production/server.js\n\n\n\n✨ It is recommend the usage of a process manager like PM2
\n
After you generate a static site, all you have to do is move the output folder to any host machine capable of serving HTML.
\n\n\n🎉 Congratulations. You are done with the advanced concepts!
\n
⚔ Learn how to use MongoDB with Nullstack.
\n","description":"With Nullstack it's easy to have your application up and running in production mode"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"How to Deploy - Nullstack","description":"With Nullstack it's easy to have your application up and running in production mode"}} \ No newline at end of file diff --git a/docs/how-to-use-facebook-pixel-with-nullstack/index.html b/docs/how-to-use-facebook-pixel-with-nullstack/index.html deleted file mode 100644 index b74ae574..00000000 --- a/docs/how-to-use-facebook-pixel-with-nullstack/index.html +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - -According to developers.facebook.com:
-"The Facebook pixel is a snippet of JavaScript code that allows you to track visitor activity on your website."
-You can take advantage of the context and custom events to create a component that dynamically sends Pixel events.
-Facebook's Pixel can only be called after hydrate to ensure it is running in the client.
-import Nullstack from 'nullstack';
-
-class FacebookPixel extends Nullstack {
-
- async hydrate({page, id}) {
- ! function(f, b, e, v, n, t, s) {
- if (f.fbq) return;
- n = f.fbq = function() {
- n.callMethod ?
- n.callMethod.apply(n, arguments) : n.queue.push(arguments)
- };
- if (!f._fbq) f._fbq = n;
- n.push = n;
- n.loaded = !0;
- n.version = '2.0';
- n.queue = [];
- t = b.createElement(e);
- t.async = !0;
- t.src = v;
- s = b.getElementsByTagName(e)[0];
- s.parentNode.insertBefore(t, s)
- }(window, document, 'script',
- 'https://connect.facebook.net/en_US/fbevents.js');
- fbq('init', id);
- fbq('track', 'PageView');
- window.addEventListener(page.event, () => {
- fbq('init', id);
- fbq('track', 'PageView');
- })
- }
-}
-
-export default FacebookPixel;
-
-import Nullstack from 'nullstack';
-import FacebookPixel from './FacebookPixel';
-
-class Application extends Nullstack {
-
- // ...
-
- render() {
- return (
- <main>
- <FacebookPixel id="REPLACE_WITH_YOUR_FACEBOOK_PIXEL_ID" />
- </main>
- )
- }
-
-
-}
-
-export default Application;
-
-Alternatively, you can install nullstack-facebook-pixel as a dependency:
-npm install nullstack-facebook-pixel
-
-import Nullstack from 'nullstack';
-import FacebookPixel from 'nullstack-facebook-pixel';
-
-class Application extends Nullstack {
-
- // ...
-
- render() {
- return (
- <main>
- <FacebookPixel id="REPLACE_WITH_YOUR_FACEBOOK_PIXEL_ID" />
- </main>
- )
- }
-
-
-}
-
-export default Application;
-
---🎉 Congratulations. You are done with the documentation!
-
⚔ If you want to see this more examples please open an issue on github.
-According to developers.facebook.com:
\n"The Facebook pixel is a snippet of JavaScript code that allows you to track visitor activity on your website."
\nYou can take advantage of the context and custom events to create a component that dynamically sends Pixel events.
\nFacebook's Pixel can only be called after hydrate to ensure it is running in the client.
\nimport Nullstack from 'nullstack';\n\nclass FacebookPixel extends Nullstack {\n\n async hydrate({page, id}) {\n ! function(f, b, e, v, n, t, s) {\n if (f.fbq) return;\n n = f.fbq = function() {\n n.callMethod ?\n n.callMethod.apply(n, arguments) : n.queue.push(arguments)\n };\n if (!f._fbq) f._fbq = n;\n n.push = n;\n n.loaded = !0;\n n.version = '2.0';\n n.queue = [];\n t = b.createElement(e);\n t.async = !0;\n t.src = v;\n s = b.getElementsByTagName(e)[0];\n s.parentNode.insertBefore(t, s)\n }(window, document, 'script',\n 'https://connect.facebook.net/en_US/fbevents.js');\n fbq('init', id);\n fbq('track', 'PageView');\n window.addEventListener(page.event, () => {\n fbq('init', id);\n fbq('track', 'PageView');\n })\n }\n}\n\nexport default FacebookPixel;\n\nimport Nullstack from 'nullstack';\nimport FacebookPixel from './FacebookPixel';\n\nclass Application extends Nullstack {\n\n // ...\n\n render() {\n return (\n <main>\n <FacebookPixel id=\"REPLACE_WITH_YOUR_FACEBOOK_PIXEL_ID\" />\n </main>\n )\n }\n\n\n}\n\nexport default Application;\n\nAlternatively, you can install nullstack-facebook-pixel as a dependency:
\nnpm install nullstack-facebook-pixel\n\nimport Nullstack from 'nullstack';\nimport FacebookPixel from 'nullstack-facebook-pixel';\n\nclass Application extends Nullstack {\n\n // ...\n\n render() {\n return (\n <main>\n <FacebookPixel id=\"REPLACE_WITH_YOUR_FACEBOOK_PIXEL_ID\" />\n </main>\n )\n }\n\n\n}\n\nexport default Application;\n\n\n\n🎉 Congratulations. You are done with the documentation!
\n
⚔ If you want to see this more examples please open an issue on github.
\n","description":"Take advantage of the [context](/context) and [custom events](/context-page) to create a component that dynamically sends Pixel events"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Facebook's Pixel - Nullstack","description":"Take advantage of the [context](/context) and [custom events](/context-page) to create a component that dynamically sends Pixel events"}} \ No newline at end of file diff --git a/docs/how-to-use-google-analytics-with-nullstack/index.html b/docs/how-to-use-google-analytics-with-nullstack/index.html deleted file mode 100644 index f05d34a3..00000000 --- a/docs/how-to-use-google-analytics-with-nullstack/index.html +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - -According to analytics.google.com:
-"Google Analytics lets you measure your advertising ROI as well as track your Flash, video, and social networking sites and applications."
-You can take advantage of the context and custom events to create a component that dynamically sends GTAG events.
-GTAG can only be called after hydrate to ensure it is running in the client.
-import Nullstack from 'nullstack';
-
-class GoogleAnalytics extends Nullstack {
-
- hydrate({router, page, id}) {
- window.dataLayer = window.dataLayer || [];
- function gtag(){
- dataLayer.push(arguments);
- }
- gtag('js', new Date());
- gtag('config', id, {
- page_title: page.title,
- page_path: router.url
- });
- window.addEventListener(page.event, () => {
- gtag('event', 'page_view', {
- page_title: page.title,
- page_path: router.url
- })
- })
- }
-
- render({id}) {
- return (
- <script
- async
- src={`https://www.googletagmanager.com/gtag/js?id=${id}`}
- />
- )
- }
-
-}
-
-export default GoogleAnalytics;
-
-import Nullstack from 'nullstack';
-import GoogleAnalytics from './GoogleAnalytics';
-
-class Application extends Nullstack {
-
- // ...
-
- render() {
- return (
- <main>
- <GoogleAnalytics id="REPLACE_WITH_YOUR_GOOGLE_ANALYTICS_ID" />
- </main>
- )
- }
-
-
-}
-
-export default Application;
-
-Alternatively, you can install nullstack-google-analytics as a dependency:
-npm install nullstack-google-analytics
-
-import Nullstack from 'nullstack';
-import GoogleAnalytics from 'nullstack-google-analytics';
-
-class Application extends Nullstack {
-
- // ...
-
- render() {
- return (
- <main>
- <GoogleAnalytics id="REPLACE_WITH_YOUR_GOOGLE_ANALYTICS_ID" />
- </main>
- )
- }
-
-
-}
-
-export default Application;
-
-According to analytics.google.com:
\n"Google Analytics lets you measure your advertising ROI as well as track your Flash, video, and social networking sites and applications."
\nYou can take advantage of the context and custom events to create a component that dynamically sends GTAG events.
\nGTAG can only be called after hydrate to ensure it is running in the client.
\nimport Nullstack from 'nullstack';\n\nclass GoogleAnalytics extends Nullstack {\n\n hydrate({router, page, id}) {\n window.dataLayer = window.dataLayer || [];\n function gtag(){\n dataLayer.push(arguments);\n }\n gtag('js', new Date());\n gtag('config', id, {\n page_title: page.title,\n page_path: router.url\n });\n window.addEventListener(page.event, () => {\n gtag('event', 'page_view', {\n page_title: page.title,\n page_path: router.url\n })\n })\n }\n \n render({id}) {\n return (\n <script \n async\n src={`https://www.googletagmanager.com/gtag/js?id=${id}`}\n />\n )\n }\n\n}\n\nexport default GoogleAnalytics;\n\nimport Nullstack from 'nullstack';\nimport GoogleAnalytics from './GoogleAnalytics';\n\nclass Application extends Nullstack {\n\n // ...\n\n render() {\n return (\n <main>\n <GoogleAnalytics id=\"REPLACE_WITH_YOUR_GOOGLE_ANALYTICS_ID\" />\n </main>\n )\n }\n\n\n}\n\nexport default Application;\n\nAlternatively, you can install nullstack-google-analytics as a dependency:
\nnpm install nullstack-google-analytics\n\nimport Nullstack from 'nullstack';\nimport GoogleAnalytics from 'nullstack-google-analytics';\n\nclass Application extends Nullstack {\n\n // ...\n\n render() {\n return (\n <main>\n <GoogleAnalytics id=\"REPLACE_WITH_YOUR_GOOGLE_ANALYTICS_ID\" />\n </main>\n )\n }\n\n\n}\n\nexport default Application;\n\n⚔ Learn how to use Facebook Pixel with Nullstack.
\n","description":"Take advantage of the context and custom events to create a component that dynamically sends GTAG events"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Google Analytics - Nullstack","description":"Take advantage of the context and custom events to create a component that dynamically sends GTAG events"}} \ No newline at end of file diff --git a/docs/how-to-use-mongodb-with-nullstack/index.html b/docs/how-to-use-mongodb-with-nullstack/index.html deleted file mode 100644 index 1aae6264..00000000 --- a/docs/how-to-use-mongodb-with-nullstack/index.html +++ /dev/null @@ -1,107 +0,0 @@ - - - - - - -According to mongodb.com:
-"MongoDB is a general purpose, document-based, distributed database built for modern application developers and for the cloud era."
-You can use any database with Nullstack, but the javascript integration and flexibility of MongoDB looks especially good with Nullstack applications.
-Install the MongoDB driver from npm:
-npm install mongodb
-
-Configure the database credentials using secrets.
-The last step is to simply assign the database connection to the server context.
-import Nullstack from 'nullstack';
-import {MongoClient} from 'mongodb';
-
-class Application extends Nullstack {
-
- static async start(context) {
- const {secrets} = context;
- secrets.development.databaseHost = 'mongodb://localhost:27017/dbname';
- secrets.databaseName = 'dbname';
- await this.startDatabase(context);
- }
-
- static async startDatabase(context) {
- const {secrets} = context;
- const databaseClient = new MongoClient(secrets.databaseHost);
- await databaseClient.connect();
- context.database = await databaseClient.db(secrets.databaseName);
- }
-
-}
-
-export default Application;
-
-The example above will make the database key available to all your server functions.
-import Nullstack from 'nullstack';
-
-class BooksList extends Nullstack {
-
- books = [];
-
- static async getBooks({database}) {
- return await database.collection('books').find().toArray();
- }
-
- async initiate() {
- this.books = await this.getBooks();
- }
-
- // ...
-
-}
-
-export default BooksList;
-
-According to mongodb.com:
\n"MongoDB is a general purpose, document-based, distributed database built for modern application developers and for the cloud era."
\nYou can use any database with Nullstack, but the javascript integration and flexibility of MongoDB looks especially good with Nullstack applications.
\nInstall the MongoDB driver from npm:
\nnpm install mongodb\n\nConfigure the database credentials using secrets.
\nThe last step is to simply assign the database connection to the server context.
\nimport Nullstack from 'nullstack';\nimport {MongoClient} from 'mongodb';\n\nclass Application extends Nullstack {\n\n static async start(context) {\n const {secrets} = context;\n secrets.development.databaseHost = 'mongodb://localhost:27017/dbname';\n secrets.databaseName = 'dbname';\n await this.startDatabase(context);\n }\n\n static async startDatabase(context) {\n const {secrets} = context;\n const databaseClient = new MongoClient(secrets.databaseHost);\n await databaseClient.connect();\n context.database = await databaseClient.db(secrets.databaseName);\n }\n\n}\n\nexport default Application;\n\nThe example above will make the database key available to all your server functions.
\nimport Nullstack from 'nullstack';\n\nclass BooksList extends Nullstack {\n\n books = [];\n\n static async getBooks({database}) {\n return await database.collection('books').find().toArray();\n }\n\n async initiate() {\n this.books = await this.getBooks();\n }\n\n // ...\n\n}\n\nexport default BooksList;\n\n⚔ Learn how to use Google Analytics with Nullstack.
\n","description":"You can use any database with Nullstack, but the javascript integration and flexibility of MongoDB looks especially good with Nullstack applications"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"How to use MongoDB - Nullstack","description":"You can use any database with Nullstack, but the javascript integration and flexibility of MongoDB looks especially good with Nullstack applications"}} \ No newline at end of file diff --git a/docs/icon-128x128.png b/docs/icon-128x128.png deleted file mode 100644 index e96fe8b5..00000000 Binary files a/docs/icon-128x128.png and /dev/null differ diff --git a/docs/icon-144x144.png b/docs/icon-144x144.png deleted file mode 100644 index a87f2a70..00000000 Binary files a/docs/icon-144x144.png and /dev/null differ diff --git a/docs/icon-152x152.png b/docs/icon-152x152.png deleted file mode 100644 index 10b23e29..00000000 Binary files a/docs/icon-152x152.png and /dev/null differ diff --git a/docs/icon-180x180.png b/docs/icon-180x180.png deleted file mode 100644 index 252b3a4f..00000000 Binary files a/docs/icon-180x180.png and /dev/null differ diff --git a/docs/icon-192x192.png b/docs/icon-192x192.png deleted file mode 100644 index 450ca4ca..00000000 Binary files a/docs/icon-192x192.png and /dev/null differ diff --git a/docs/icon-384x384.png b/docs/icon-384x384.png deleted file mode 100644 index c50c7037..00000000 Binary files a/docs/icon-384x384.png and /dev/null differ diff --git a/docs/icon-512x512.png b/docs/icon-512x512.png deleted file mode 100644 index db7461c0..00000000 Binary files a/docs/icon-512x512.png and /dev/null differ diff --git a/docs/icon-72x72.png b/docs/icon-72x72.png deleted file mode 100644 index 58aaf4cd..00000000 Binary files a/docs/icon-72x72.png and /dev/null differ diff --git a/docs/icon-96x96.png b/docs/icon-96x96.png deleted file mode 100644 index a6dd92a8..00000000 Binary files a/docs/icon-96x96.png and /dev/null differ diff --git a/docs/image-1200x630.png b/docs/image-1200x630.png deleted file mode 100644 index 03da34b6..00000000 Binary files a/docs/image-1200x630.png and /dev/null differ diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index 918a4415..00000000 --- a/docs/index.html +++ /dev/null @@ -1,218 +0,0 @@ - - - - - - -for one-dev armies
Nullstack is a full-stack framework for building progressive web applications.
It connects a stateful UI layer to specialized microservices in the same component using vanilla javascript.
Focus on solving your business logic instead of writing glue code.
Nullstack generates SEO ready HTML optimized for the first paint of your route in a single request using local functions with zero javascript dependencies in the client bundle.
After hydration, requests will fetch JSON from an automatically generated API off server functions, update the application state, and rerender the page.
You can even use Nullstack to generate lightning-fast static websites that serve HTML and become a single page application using an automatically generated static API .
Nullstack is not another part of your stack, it is your stack
Your application can be exported from back-end to front-end as a component and mounted into another application
import Nullstack from 'nullstack';
-import TaskList from './TaskList';
-import {readFileSync} from 'fs';
-
-class Application extends Nullstack {
-
- static async getTasks({limit}) {
- const json = readFileSync('tasks.json', 'utf-8');
- return JSON.parse(json).tasks.slice(0, limit);
- }
-
- prepare(context) {
- context.tasks = [];
- }
-
- async initiate(context) {
- context.tasks = await this.getTasks({limit: 10});
- }
-
- render() {
- return (
- <main>
- <TaskList route="/tasks" />
- <a href="/tasks" route="*"> List Tasks </a>
- </main>
- )
- }
-
-}
-
-export default Application;import Nullstack from 'nullstack';
-
-class TaskList extends Nullstack {
-
- hideComplete = false;
-
- renderTask({task}) {
- if(this.hideComplete && task.complete) return false;
- return (
- <li>
- <input bind={task.description} />
- </li>
- )
- }
-
- render({tasks}) {
- return (
- <div>
- <ul>
- {tasks.map((task) => <Task task={task} />)}
- </ul>
- <button onclick={{hideComplete: !this.hideComplete}}>
- Toggle complete tasks
- </button>
- </div>
- )
- }
-
-}
-
-export default TaskList;The example above uses server functions to read tasks from a JSON file and store them in the context available to all components.
The tasks are listed in a specific route that renders a component with multiple inner components filtered by status with inputs using two-way bindings .
Nullstack features have been extracted from real life projects with convenience and consistency in mind
import Nullstack from 'nullstack';
-
-class Controlled extends Nullstack {
-
- count = 0;
-
- increment({delta}) {
- this.count += delta;
- }
-
- render() {
- return (
- <div>
- <button onclick={this.increment} delta={-1}>
- {this.count}
- </button>
- <span> {this.count} </span>
- <button onclick={this.increment} delta={1}>
- {this.count}
- </button>
- </div>
- )
- }
-
-}
-
-export default Controlled;import Nullstack from 'nullstack';
-
-class Binding extends Nullstack {
-
- number = 1;
- boolean = true;
-
- object = {number: 1};
- array = ['a', 'b', 'c'];
-
- render({params}) {
- return (
- <form>
- <input bind={this.number} />
- <input bind={this.boolean} type="checkbox" />
- <input bind={this.object.number} />
- {this.array.map((value, index) => (
- <input bind={this.array[index]} />
- ))}
- <input bind={params.page} />
- </form>
- )
- }
-
-}
-
-export default Binding;import Nullstack from 'nullstack';
-
-class Routes extends Nullstack {
-
- renderPost({params}) {
- return (
- <div>
- <div route="/post/getting-started">
- npx create-nullstack-app name
- </div>
- <div route="*"> {params.slug} </div>
- </div>
- )
- }
-
- render() {
- return (
- <div>
- <Post route="/post/:slug" />
- <a href="/post/hello-world"> Welcome </a>
- </div>
- )
- }
-
-}
-
-export default Routes;import Nullstack from 'nullstack';
-
-class Lifecycle extends Nullstack {
-
- prepare({environment}) {
- const {server, client} = environment;
- }
-
- async initiate({environment}) {
- const {server, client} = environment;
- }
-
- async hydrate({environment}) {
- const {client} = environment;
- }
-
- async update({environment}) {
- const {client} = environment;
- }
-
- async terminate({environment}) {
- const {client} = environment;
- }
-
-}
-
-export default Lifecycle;Nullstack cares about making its content as direct to the point and easy to understand as possible
Every project starts small and becomes complex over time. Scale as you go, no matter the size of the team.
No compromises, no enforcements.Development of both back and front ends of a feature in the same component in an organized way with ease of overview.
True componentization and code reusability.Takes advantage of any isomorphic vanilla Javascript package made throughout history.
All of your application speaks the same language.The horizontal structure, as opposed to a hierarchical one, makes it a lot easier to move resources around.
Flexibility over bureaucracy.The instance key is a string in the framework store part of your context and allows you to persist the instance when it moves in the dom.
-This key is readonly after you assign the attribute and available only in the client context.
-You can declare one key per instance.
---💡 If you do not declare a key nullstack will generate one based on dom depth.
-
--🔥 Keys cannot start with "_." to avoid conflicts with Nullstack generated keys
-
Keys must be globally unique since the component could move anywhere around the dom and not only between its siblings.
-Keys are useful to preserve state in stateful components when you move them in the dom.
-This is especially useful for dynamically sized lists that invoke components.
-import Nullstack from 'nullstack';
-import Item from './Item';
-
-class List extends Nullstack {
-
- // ...
-
- async initiate() {
- this.items = await this.getItems();
- }
-
- render({self}) {
- return (
- <ul>
- {this.items.map((item) => (
- <Item key={item.id} {...item} />
- ))}
- </ul>
- )
- }
-
-}
-
-export default Page;
-
-You can also use keys to share the instance between two elements.
-Only the first encounter of the key will run its lifecycle
-import Nullstack from 'nullstack';
-
-class Counter extends Nullstack {
-
- count = 0;
-
- render({amount}) {
- return (
- <div>
- <button onclick={{count: this.count+1}}>
- {this.count} x {amount} = {this.count * amount}
- </button>
- </div>
- )
- }
-
-}
-
-export default Counter;
-
-import Nullstack from 'nullstack';
-import Counter from './Counter';
-
-class Application extends Nullstack {
-
- render() {
- return (
- <main>
- <Counter key="a" amount={1} />
- <Counter key="b" amount={2} />
- <Counter key="b" amount={3} />
- </main>
- )
- }
-
-}
-
-export default Application;
-
-⚔ Learn about the server request and response.
-The instance key is a string in the framework store part of your context and allows you to persist the instance when it moves in the dom.
\nThis key is readonly after you assign the attribute and available only in the client context.
\nYou can declare one key per instance.
\n\n\n💡 If you do not declare a key nullstack will generate one based on dom depth.
\n
\n\n🔥 Keys cannot start with "_." to avoid conflicts with Nullstack generated keys
\n
Keys must be globally unique since the component could move anywhere around the dom and not only between its siblings.
\nKeys are useful to preserve state in stateful components when you move them in the dom.
\nThis is especially useful for dynamically sized lists that invoke components.
\nimport Nullstack from 'nullstack';\nimport Item from './Item';\n\nclass List extends Nullstack {\n\n // ...\n\n async initiate() {\n this.items = await this.getItems();\n }\n \n render({self}) {\n return (\n <ul> \n {this.items.map((item) => (\n <Item key={item.id} {...item} />\n ))}\n </ul>\n )\n }\n\n}\n\nexport default Page;\n\nYou can also use keys to share the instance between two elements.
\nOnly the first encounter of the key will run its lifecycle
\nimport Nullstack from 'nullstack';\n\nclass Counter extends Nullstack {\n\n count = 0;\n\n render({amount}) {\n return (\n <div>\n <button onclick={{count: this.count+1}}>\n {this.count} x {amount} = {this.count * amount}\n </button> \n </div>\n )\n }\n\n}\n\nexport default Counter;\n\nimport Nullstack from 'nullstack';\nimport Counter from './Counter';\n\nclass Application extends Nullstack {\n\n render() {\n return (\n <main>\n <Counter key=\"a\" amount={1} />\n <Counter key=\"b\" amount={2} />\n <Counter key=\"b\" amount={3} />\n </main>\n )\n }\n\n}\n\nexport default Application;\n\n⚔ Learn about the server request and response.
\n","description":"The instance key is a string in the framework store part of your context and allows you to persist the instance when it moves in the dom"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Instance Key - Nullstack","description":"The instance key is a string in the framework store part of your context and allows you to persist the instance when it moves in the dom"}} \ No newline at end of file diff --git a/docs/instance-self/index.html b/docs/instance-self/index.html deleted file mode 100644 index e893ebff..00000000 --- a/docs/instance-self/index.html +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - -The self object is a proxy in the framework store part of your context and gives you information about the instance lifecycle.
-This key is readonly and available only in the client context.
-Each instance receives its own self object.
-The following keys are available in the object:
-When a lifecycle method is resolved, even if not declared, an equivalent key is set to true in self.
-If the component was server-side rendered the prerendered key will remain true until it is terminated.
-The element key points to the DOM selector and is only guaranteed to exist when hydrate is being called since prepare and initiate could run in the server.
---💡 Do not use element to guess the environment, instead use the environment for that.
-
Observing self is a nice way to avoid giving placeholder information to the end-user.
-import Nullstack from 'nullstack';
-
-class Page extends Nullstack {
-
- // ...
-
- async initiate() {
- this.price = await this.getPrice();
- }
-
- async hydrate({self}) {
- self.element.querySelector('input').focus();
- }
-
- render({self}) {
- if(!self.prerendered && !self.initiated) return false;
- return (
- <form>
- <input type="number" bind={this.price} />
- <button disabled={!self.hydrated}>
- Save
- </button>
- </form>
- )
- }
-
-}
-
-export default Page;
-
---💡 Components that get optimized into functional components have no access to self.
-
⚔ Learn about the instance key.
-The self object is a proxy in the framework store part of your context and gives you information about the instance lifecycle.
\nThis key is readonly and available only in the client context.
\nEach instance receives its own self object.
\nThe following keys are available in the object:
\nWhen a lifecycle method is resolved, even if not declared, an equivalent key is set to true in self.
\nIf the component was server-side rendered the prerendered key will remain true until it is terminated.
\nThe element key points to the DOM selector and is only guaranteed to exist when hydrate is being called since prepare and initiate could run in the server.
\n\n\n💡 Do not use element to guess the environment, instead use the environment for that.
\n
Observing self is a nice way to avoid giving placeholder information to the end-user.
\nimport Nullstack from 'nullstack';\n\nclass Page extends Nullstack {\n\n // ...\n\n async initiate() {\n this.price = await this.getPrice();\n }\n\n async hydrate({self}) {\n self.element.querySelector('input').focus();\n }\n \n render({self}) {\n if(!self.prerendered && !self.initiated) return false;\n return (\n <form> \n <input type=\"number\" bind={this.price} />\n <button disabled={!self.hydrated}> \n Save\n </button>\n </form>\n )\n }\n\n}\n\nexport default Page;\n\n\n\n💡 Components that get optimized into functional components have no access to self.
\n
⚔ Learn about the instance key.
\n","description":"The self object is a proxy in the framework store part of your context and gives you information about the instance lifecycle"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Instance Self - Nullstack","description":"The self object is a proxy in the framework store part of your context and gives you information about the instance lifecycle"}} \ No newline at end of file diff --git a/docs/manifest-f0cee0769c64977a287dddeaae84c7f6.json b/docs/manifest-f0cee0769c64977a287dddeaae84c7f6.json deleted file mode 100644 index 3b29c313..00000000 --- a/docs/manifest-f0cee0769c64977a287dddeaae84c7f6.json +++ /dev/null @@ -1 +0,0 @@ -{"name":"Nullstack","short_name":"Nullstack","theme_color":"#d22365","background_color":"#2d3748","display":"standalone","orientation":"portrait","scope":"/","start_url":"/","icons":[{"src":"/icon-72x72.png","sizes":"72x72","type":"image/png","purpose":"maskable any"},{"src":"/icon-96x96.png","sizes":"96x96","type":"image/png","purpose":"maskable any"},{"src":"/icon-128x128.png","sizes":"128x128","type":"image/png","purpose":"maskable any"},{"src":"/icon-144x144.png","sizes":"144x144","type":"image/png","purpose":"maskable any"},{"src":"/icon-152x152.png","sizes":"152x152","type":"image/png","purpose":"maskable any"},{"src":"/icon-180x180.png","sizes":"180x180","type":"image/png","purpose":"maskable any"},{"src":"/icon-192x192.png","sizes":"192x192","type":"image/png","purpose":"maskable any"},{"src":"/icon-384x384.png","sizes":"384x384","type":"image/png","purpose":"maskable any"},{"src":"/icon-512x512.png","sizes":"512x512","type":"image/png","purpose":"maskable any"}],"splash_pages":null} \ No newline at end of file diff --git a/docs/njs-file-extension/index.html b/docs/njs-file-extension/index.html deleted file mode 100644 index 7e8b03ba..00000000 --- a/docs/njs-file-extension/index.html +++ /dev/null @@ -1,212 +0,0 @@ - - - - - - -Nullstack Javascript files let Webpack know which loaders to use at transpile time.
-NJS files must import Nullstack or one of its subclasses.
-If only a subclass is imported, a Nullstack import will be injected at transpile time.
-At transpile time JSX tags will be replaced with Nullstack.element
-This extension also allows Nullstack to make free transpile time optimizations like source injection and extracting renderable components into stateless functions.
---🔥 Each file must have only one class declaration.
-
On the server bundle static async functions are mapped into a registry for security.
-On the client bundle static async functions are removed and replaced with a flag.
-On the client bundle static async functions with the name starting with "start" (and optionally followed by an uppercase letter) are completely removed.
-On both server and client bundles, a hash with the md5 of the original source code is added to the class.
---🐱💻 Bellow an example of a original .njs file.
-
import List from './List';
-import {readFileSync} from 'fs';
-
-class Tasks extends List {
-
- static async getTasks({limit}) {
- const json = readFileSync('tasks.json', 'utf-8');
- return JSON.parse(json).tasks.slice(0, limit);
- }
-
- prepare(context) {
- context.tasks = [];
- }
-
- async initiate(context) {
- context.tasks = await this.getTasks({limit: 10});
- }
-
- renderTask({task}) {
- return (
- <li>
- <input bind={task.description} />
- </li>
- )
- }
-
- render() {
- return (
- <main>
- <ul>
- {tasks.map((task) => <Task task={task} />)}
- </ul>
- </main>
- )
- }
-
-}
-
-export default Tasks;
-
---🐱💻 Bellow an example of the same transpiled .njs file.
-
import Nullstack from 'nullstack';
-import List from './List';
-
-class Tasks extends List {
-
- static hash = 'd493ac09d0d57574a30f136d31da455f';
-
- static getTasks = true;
-
- prepare(context) {
- context.tasks = [];
- }
-
- async initiate(context) {
- context.tasks = await this.getTasks({limit: 10});
- }
-
- renderTask({task}) {
- return (
- <li>
- <input source={task} bind="description" />
- </li>
- )
- }
-
- render() {
- const Task = this.renderTask;
- return (
- <main>
- <ul>
- {tasks.map((task) => <Task task={task} />)}
- </ul>
- </main>
- )
- }
-
-}
-
-export default Tasks;
-
---🐱💻 Bellow an example of a original .njs file.
-
import Nullstack from 'nullstack';
-
-class Tasks extends Nullstack {
-
- renderTask({task}) {
- return (
- <li>
- <input bind={task.description} />
- </li>
- )
- }
-
- render({tasks}) {
- return (
- <main>
- <ul>
- {tasks.map((task) => <Task task={task} />)}
- </ul>
- </main>
- )
- }
-
-}
-
-export default Tasks;
-
---🐱💻 Bellow an example of the same transpiled .njs file.
-
import Nullstack from 'nullstack';
-
-class Tasks extends Nullstack {
-
- static renderTask({task}) {
- return (
- <li>
- <input source={task} bind="description" />
- </li>
- )
- }
-
- static render({tasks}) {
- const Task = this.renderTask;
- return (
- <main>
- <ul>
- {tasks.map((task) => <Task task={task} />)}
- </ul>
- </main>
- )
- }
-
-}
-
-export default Tasks;
-
-⚔ Learn about server-side rendering.
-Nullstack Javascript files let Webpack know which loaders to use at transpile time.
\nNJS files must import Nullstack or one of its subclasses.
\nIf only a subclass is imported, a Nullstack import will be injected at transpile time.
\nAt transpile time JSX tags will be replaced with Nullstack.element
\nThis extension also allows Nullstack to make free transpile time optimizations like source injection and extracting renderable components into stateless functions.
\n\n\n🔥 Each file must have only one class declaration.
\n
On the server bundle static async functions are mapped into a registry for security.
\nOn the client bundle static async functions are removed and replaced with a flag.
\nOn the client bundle static async functions with the name starting with "start" (and optionally followed by an uppercase letter) are completely removed.
\nOn both server and client bundles, a hash with the md5 of the original source code is added to the class.
\n\n\n🐱💻 Bellow an example of a original .njs file.
\n
import List from './List';\nimport {readFileSync} from 'fs';\n\nclass Tasks extends List {\n\n static async getTasks({limit}) {\n const json = readFileSync('tasks.json', 'utf-8');\n return JSON.parse(json).tasks.slice(0, limit);\n }\n\n prepare(context) {\n context.tasks = [];\n }\n\n async initiate(context) {\n context.tasks = await this.getTasks({limit: 10});\n }\n\n renderTask({task}) {\n return (\n <li> \n <input bind={task.description} />\n </li>\n )\n }\n\n render() {\n return (\n <main>\n <ul>\n {tasks.map((task) => <Task task={task} />)}\n </ul>\n </main>\n )\n }\n\n}\n\nexport default Tasks;\n\n\n\n🐱💻 Bellow an example of the same transpiled .njs file.
\n
import Nullstack from 'nullstack';\nimport List from './List';\n\nclass Tasks extends List {\n\n static hash = 'd493ac09d0d57574a30f136d31da455f';\n\n static getTasks = true;\n\n prepare(context) {\n context.tasks = [];\n }\n\n async initiate(context) {\n context.tasks = await this.getTasks({limit: 10});\n }\n\n renderTask({task}) {\n return (\n <li> \n <input source={task} bind=\"description\" />\n </li>\n )\n }\n\n render() {\n const Task = this.renderTask;\n return (\n <main>\n <ul>\n {tasks.map((task) => <Task task={task} />)}\n </ul>\n </main>\n )\n }\n\n}\n\nexport default Tasks;\n\n\n\n🐱💻 Bellow an example of a original .njs file.
\n
import Nullstack from 'nullstack';\n\nclass Tasks extends Nullstack {\n\n renderTask({task}) {\n return (\n <li> \n <input bind={task.description} />\n </li>\n )\n }\n\n render({tasks}) {\n return (\n <main>\n <ul>\n {tasks.map((task) => <Task task={task} />)}\n </ul>\n </main>\n )\n }\n\n}\n\nexport default Tasks;\n\n\n\n🐱💻 Bellow an example of the same transpiled .njs file.
\n
import Nullstack from 'nullstack';\n\nclass Tasks extends Nullstack {\n\n static renderTask({task}) {\n return (\n <li> \n <input source={task} bind=\"description\" />\n </li>\n )\n }\n\n static render({tasks}) {\n const Task = this.renderTask;\n return (\n <main>\n <ul>\n {tasks.map((task) => <Task task={task} />)}\n </ul>\n </main>\n )\n }\n\n}\n\nexport default Tasks;\n\n⚔ Learn about server-side rendering.
\n","description":"Nullstack Javascript files let Webpack know which loaders to use at transpile time"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"NJS File Extension - Nullstack","description":"Nullstack Javascript files let Webpack know which loaders to use at transpile time"}} \ No newline at end of file diff --git a/docs/nullachan.png b/docs/nullachan.png deleted file mode 100644 index 0f5ccd45..00000000 Binary files a/docs/nullachan.png and /dev/null differ diff --git a/docs/nullstack.png b/docs/nullstack.png deleted file mode 100644 index a3fe1254..00000000 Binary files a/docs/nullstack.png and /dev/null differ diff --git a/docs/nullstack.svg b/docs/nullstack.svg deleted file mode 100644 index edd08d14..00000000 --- a/docs/nullstack.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/offline-f0cee0769c64977a287dddeaae84c7f6/index.html b/docs/offline-f0cee0769c64977a287dddeaae84c7f6/index.html deleted file mode 100644 index 6e8380e1..00000000 --- a/docs/offline-f0cee0769c64977a287dddeaae84c7f6/index.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - -Perhaps you want to learn abouthow to make a 404 page with Nullstack?
If you are looking for something else, you shouldread the documentation.
The simplest component you can make is a renderable component.
-Renderable components are very similar to web components, they give you the ability to create new HTML tags that shortcut a group of other HTML tags.
-Create a file in your src folder with the name of your component and the njs extension.
-In this example it is going to be called HelloWorld.njs.
-All you have to do is to import Nullstack or any of its subclasses and extend your class from it, define an instance method called render that returns any JSX, and export the component.
---✨ Install the official Nullstack VSCode Extension to generate classes with a snippet.
-
import Nullstack from 'nullstack';
-
-class HelloWorld extends Nullstack {
-
- render() {
- return (
- <div> Hello World </div>
- )
- }
-
-}
-
-export default HelloWorld;
-
-The code above is just declaring the component, you still have to use it.
-Importing the component in your application gives you the ability to use a new tag in your render.
-This tag will be replaced with whatever you returned in your component render.
-import Nullstack from 'nullstack';
-
-import './Application.scss';
-
-import HelloWorld from './HelloWorld';
-
-class Application extends Nullstack {
-
- // ...
-
- render({page}) {
- return (
- <main>
- <h1> {page.title} </h1>
- <a href="https://nullstack.app/documentation" target="_blank"> Read the documentation </a>
- <HelloWorld />
- </main>
- )
- }
-
-}
-
-export default Application;
-
---💡 Components that do nothing besides rendering are extracted into faster functional components at transpile time!
-
Nullstack JSX deviates a little from the spec.
-You can use the normal HTML attributes like class and for directly.
-<label for="input" class="dont-label-me"> I am a label </label>
-
-If you want to skip rendering the component at all you can simply return false from the render.
-import Nullstack from 'nullstack';
-
-class Headless extends Nullstack {
-
- render() {
- return false;
- }
-
-}
-
-export default Headless;
-
-This will allocate DOM space for when you decide to render markup there.
-This is also useful for conditional rendering.
-If all you want to do is to generate an invisible component you can skip defining the render method at all.
-Instead of creating a new component just to organize code-splitting, you can create an inner component.
-Inner components are any method that the name starts with render followed by an uppercase character.
-Inner components share the same instance and scope as the main component, therefore, are very convenient to avoid problems like props drilling.
-To invoke the inner component use a JSX tag with the method name without the render prefix.
-import Nullstack from 'nullstack';
-
-class Post extends Nullstack {
-
- renderArticle() {
- return (
- <article> Content </article>
- )
- }
-
- renderAside() {
- return (
- <aside> Related content </aside>
- )
- }
-
- render() {
- return (
- <div>
- <Article />
- <Aside />
- </div>
- )
- }
-
-}
-
-export default HelloWorld;
-
---💡 Nullstack will inject a constant reference to the function at transpile time in order to completely skip the runtime lookup process!
-
Attributes can be assigned as a boolean.
-When the value is false the attribute will not be rendered at all.
-When the value is true it will be rendered as a boolean attribute without a string value.
-<button disabled={false}> Button </button>
-
-You can shortcut attributes when you know the value will always be true.
-<button disabled> Button </button>
-
---✨ Learn more about attributes.
-
If you need to decide the tag name at runtime, you can use the element tag and set the tag attribute conditionally.
-<element tag={!!link ? 'a' : 'span'} href={link || false}>
- some arbitrary text
-</element>
-
-When the tag attribute is omitted, Nullstack will default to a div.
-SVG can be used as if it were any regular HTML tag.
-You can manipulate the SVG using attributes and events normally.
-<svg height={this.size} viewBox="0 0 100 100">
- <circle cx="50" cy="50" r="40" onclick={this.grow} />
-</svg>
-
---✨ Learn more about events.
-
Your component can be invoked passing a block of content.
-<Header>
- <h1> Hello World </h1>
-</Header>
-
-This doesn't automatically render the block since it wouldn't know where to place it.
-You can destructure the children on the render method and place it in your markup.
-import Nullstack from 'nullstack';
-
-class Header extends Nullstack {
-
- render({children}) {
- return (
- <div>{children}</div>
- )
- }
-
-}
-
-export default Header;
-
---✨ This is possible because the children key is part of the instance context.
-
You can map over lists without declaring a key.
-Lists that may change length must be wrapped in a parent element just for them.
-<ul>
- {list.map((item) => <li>{item.name}</li>)}
-</ul>
-
-You can emulate a fixed-size list by returning false instead of an element to reserve dom space.
-{list.map((item) => (
- item.visible ? <div>{item.name}</div> : false
-)}
-
-It's a nice practice to use inner components combined with lists to clean up your code.
-import Nullstack from 'nullstack';
-
-class List extends Nullstack {
-
- items = [
- {visible: true, number: 1},
- {visible: false, number: 2},
- {visible: true, number: 3}
- ]
-
- renderItem({visible, number}) {
- if(!visible) return false;
- return (
- <li> {number} </li>
- )
- }
-
- render() {
- return (
- <ul>
- {this.items.map((item) => <Item {...item} />)}
- </ul>
- )
- }
-
-}
-
-export default List;
-
---✨ Sometimes you will notice keys in the map. Learn more about the instance key.
-
You can set the inner HTML of an element with the html attribute.
-Links inside the HTML string will be replaced with routable anchors.
-import Nullstack from 'nullstack';
-
-class Post extends Nullstack {
-
- content = `
- <h1> This is a Post </h1>
- <a href="/other-post">
- Check this other post
- </a>
- `;
-
- render() {
- return (
- <article html={this.content} />
- )
- }
-
-}
-
-export default Post;
-
---🔥 Be careful! When using user-generated HTML you are in risk of script injection
-
Renderable components can render inside the head tag an unlimited number of times at any depth of the application.
-The head tag will only be updated during the server-side rendering process and changes will be ignored after the hydration process.
-import Nullstack from 'nullstack';
-
-class Application extends Nullstack {
-
- // ...
-
- render() {
- return (
- <main>
- <div>
- <head>
- <link rel="preconnect" href="https://www.googletagmanager.com" />
- </head>
- </div>
- <head>
- <link rel="preload" href="/roboto-v20-latin-300.woff2" as="font" type="font/woff2" crossorigin />
- <link rel="preload" href="/crete-round-v9-latin-regular.woff2" as="font" type="font/woff2" crossorigin />
- </head>
- </main>
- )
- }
-
-}
-
-export default Application;
-
---🔥 you should not use the head tag to update metatags that Nullstack already controls
-
Currently, Nullstack doesn't support JSX Fragments. If you want to see this feature implemented please open an issue on github.
-⚔ Add state to your component using stateful components.
-The simplest component you can make is a renderable component.
\nRenderable components are very similar to web components, they give you the ability to create new HTML tags that shortcut a group of other HTML tags.
\nCreate a file in your src folder with the name of your component and the njs extension.
\nIn this example it is going to be called HelloWorld.njs.
\nAll you have to do is to import Nullstack or any of its subclasses and extend your class from it, define an instance method called render that returns any JSX, and export the component.
\n\n\n✨ Install the official Nullstack VSCode Extension to generate classes with a snippet.
\n
import Nullstack from 'nullstack';\n\nclass HelloWorld extends Nullstack {\n \n render() {\n return (\n <div> Hello World </div>\n )\n }\n\n}\n\nexport default HelloWorld;\n\nThe code above is just declaring the component, you still have to use it.
\nImporting the component in your application gives you the ability to use a new tag in your render.
\nThis tag will be replaced with whatever you returned in your component render.
\nimport Nullstack from 'nullstack';\n\nimport './Application.scss';\n\nimport HelloWorld from './HelloWorld';\n\nclass Application extends Nullstack {\n\n // ...\n\n render({page}) {\n return (\n <main>\n <h1> {page.title} </h1>\n <a href=\"https://nullstack.app/documentation\" target=\"_blank\"> Read the documentation </a>\n <HelloWorld />\n </main>\n )\n }\n\n}\n\nexport default Application;\n\n\n\n💡 Components that do nothing besides rendering are extracted into faster functional components at transpile time!
\n
Nullstack JSX deviates a little from the spec.
\nYou can use the normal HTML attributes like class and for directly.
\n<label for=\"input\" class=\"dont-label-me\"> I am a label </label>\n\nIf you want to skip rendering the component at all you can simply return false from the render.
\nimport Nullstack from 'nullstack';\n\nclass Headless extends Nullstack {\n \n render() {\n return false;\n }\n\n}\n\nexport default Headless;\n\nThis will allocate DOM space for when you decide to render markup there.
\nThis is also useful for conditional rendering.
\nIf all you want to do is to generate an invisible component you can skip defining the render method at all.
\nInstead of creating a new component just to organize code-splitting, you can create an inner component.
\nInner components are any method that the name starts with render followed by an uppercase character.
\nInner components share the same instance and scope as the main component, therefore, are very convenient to avoid problems like props drilling.
\nTo invoke the inner component use a JSX tag with the method name without the render prefix.
\nimport Nullstack from 'nullstack';\n\nclass Post extends Nullstack {\n\n renderArticle() {\n return (\n <article> Content </article>\n )\n }\n\n renderAside() {\n return (\n <aside> Related content </aside>\n )\n }\n \n render() {\n return (\n <div>\n <Article />\n <Aside />\n </div>\n )\n }\n\n}\n\nexport default HelloWorld;\n\n\n\n💡 Nullstack will inject a constant reference to the function at transpile time in order to completely skip the runtime lookup process!
\n
Attributes can be assigned as a boolean.
\nWhen the value is false the attribute will not be rendered at all.
\nWhen the value is true it will be rendered as a boolean attribute without a string value.
\n<button disabled={false}> Button </button>\n\nYou can shortcut attributes when you know the value will always be true.
\n<button disabled> Button </button>\n\n\n\n✨ Learn more about attributes.
\n
If you need to decide the tag name at runtime, you can use the element tag and set the tag attribute conditionally.
\n<element tag={!!link ? 'a' : 'span'} href={link || false}>\n some arbitrary text\n</element>\n\nWhen the tag attribute is omitted, Nullstack will default to a div.
\nSVG can be used as if it were any regular HTML tag.
\nYou can manipulate the SVG using attributes and events normally.
\n<svg height={this.size} viewBox=\"0 0 100 100\">\n <circle cx=\"50\" cy=\"50\" r=\"40\" onclick={this.grow} />\n</svg> \n\n\n\n✨ Learn more about events.
\n
Your component can be invoked passing a block of content.
\n<Header> \n <h1> Hello World </h1>\n</Header>\n\nThis doesn't automatically render the block since it wouldn't know where to place it.
\nYou can destructure the children on the render method and place it in your markup.
\nimport Nullstack from 'nullstack';\n\nclass Header extends Nullstack {\n \n render({children}) {\n return (\n <div>{children}</div>\n )\n }\n\n}\n\nexport default Header;\n\n\n\n✨ This is possible because the children key is part of the instance context.
\n
You can map over lists without declaring a key.
\nLists that may change length must be wrapped in a parent element just for them.
\n<ul>\n {list.map((item) => <li>{item.name}</li>)}\n</ul>\n\nYou can emulate a fixed-size list by returning false instead of an element to reserve dom space.
\n{list.map((item) => (\n item.visible ? <div>{item.name}</div> : false\n)}\n\nIt's a nice practice to use inner components combined with lists to clean up your code.
\nimport Nullstack from 'nullstack';\n\nclass List extends Nullstack {\n\n items = [\n {visible: true, number: 1},\n {visible: false, number: 2},\n {visible: true, number: 3}\n ]\n\n renderItem({visible, number}) {\n if(!visible) return false;\n return (\n <li> {number} </li>\n )\n }\n \n render() {\n return (\n <ul>\n {this.items.map((item) => <Item {...item} />)}\n </ul>\n )\n }\n\n}\n\nexport default List;\n\n\n\n✨ Sometimes you will notice keys in the map. Learn more about the instance key.
\n
You can set the inner HTML of an element with the html attribute.
\nLinks inside the HTML string will be replaced with routable anchors.
\nimport Nullstack from 'nullstack';\n\nclass Post extends Nullstack {\n\n content = `\n <h1> This is a Post </h1>\n <a href=\"/other-post\">\n Check this other post\n </a>\n `;\n \n render() {\n return (\n <article html={this.content} />\n )\n }\n\n}\n\nexport default Post;\n\n\n\n🔥 Be careful! When using user-generated HTML you are in risk of script injection
\n
Renderable components can render inside the head tag an unlimited number of times at any depth of the application.
\nThe head tag will only be updated during the server-side rendering process and changes will be ignored after the hydration process.
\nimport Nullstack from 'nullstack';\n\nclass Application extends Nullstack {\n\n // ...\n\n render() {\n return (\n <main>\n <div>\n <head>\n <link rel=\"preconnect\" href=\"https://www.googletagmanager.com\" />\n </head>\n </div>\n <head>\n <link rel=\"preload\" href=\"/roboto-v20-latin-300.woff2\" as=\"font\" type=\"font/woff2\" crossorigin />\n <link rel=\"preload\" href=\"/crete-round-v9-latin-regular.woff2\" as=\"font\" type=\"font/woff2\" crossorigin />\n </head>\n </main>\n )\n }\n\n}\n\nexport default Application;\n\n\n\n🔥 you should not use the head tag to update metatags that Nullstack already controls
\n
Currently, Nullstack doesn't support JSX Fragments. If you want to see this feature implemented please open an issue on github.
\n⚔ Add state to your component using stateful components.
\n","description":"Renderable components are very similar to web components they give you the ability to create new HTML tags that shortcut a group of other HTML tags"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Renderable Components - Nullstack","description":"Renderable components are very similar to web components they give you the ability to create new HTML tags that shortcut a group of other HTML tags"}} \ No newline at end of file diff --git a/docs/roboto-v20-latin-300.woff2 b/docs/roboto-v20-latin-300.woff2 deleted file mode 100644 index ef8c8836..00000000 Binary files a/docs/roboto-v20-latin-300.woff2 and /dev/null differ diff --git a/docs/roboto-v20-latin-500.woff2 b/docs/roboto-v20-latin-500.woff2 deleted file mode 100644 index 6362d7f6..00000000 Binary files a/docs/roboto-v20-latin-500.woff2 and /dev/null differ diff --git a/docs/robots.txt b/docs/robots.txt deleted file mode 100644 index 95a840fc..00000000 --- a/docs/robots.txt +++ /dev/null @@ -1,3 +0,0 @@ -User-Agent: * -Allow: / -Sitemap: https://nullstack.app/sitemap.xml \ No newline at end of file diff --git a/docs/routes-and-params/index.html b/docs/routes-and-params/index.html deleted file mode 100644 index ae8ce661..00000000 --- a/docs/routes-and-params/index.html +++ /dev/null @@ -1,300 +0,0 @@ - - - - - - -Nullstack has built-in routes, it would make no sense otherwise since web applications are expected to have hyperlinks.
-Any tag can receive a route attribute, be it a component, inner component, or simple HTML tag.
-import Nullstack from 'nullstack';
-import Page from './Page';
-
-class Application extends Nullstack {
-
- renderHome() {
- return (
- <section> Home </section>
- )
- }
-
- render({count}) {
- return (
- <main>
- <Home route="/" />
- <Page route="/page">
- <abbr route="/abbreviations"> Abbreviations </abbr>
- </main>
- )
- }
-
-}
-
-export default Application;
-
-Links on Nullstack are simple a tags with the href value starting with "/".
-<a href="/page/about"> About Page </a>
-
---💡 On the client side the click event will push history without reloading the page.
-
--✨ You can still assign your own click event to the tag without losing the framework behavior.
-
The params key is an object proxy injected into every client instance.
-Each query string param is mapped to this object.
-By default any key you request from this object will return a string.
-If the value is undefined it will return an empty string.
-If the value is true or false it will return a boolean instead.
---🐱💻 Bellow an exemple that visits "/books?expanded=true&page=2":
-
import Nullstack from 'nullstack';
-
-class Books extends Nullstack {
-
- async initiate({params}) {
- if(params.expanded) {
- const page = parseInt(params.page) || 1;
- this.books = await this.getBooks({page});
- }
- }
-
-}
-
-export default Books;
-
-Assigning to a params key will cause a redirect to the route with updated params.
-When you assign to a param, the value will be converted to JSON before being set.
---💡 Redirects work in batches, so there is no performance loss in multiple assignments.
-
import Nullstack from 'nullstack';
-
-class Paginator extends Nullstack {
-
- handleClick({params}) {
- params.filter = '';
- params.page = 1;
- }
-
-}
-
-export default Paginator;
-
-Assigning an empty string to a param will remove it from the url.
-Part of the route can be an expression started with ":" followed by a param name.
-This value will be matched against any string in the same directory position.
-The value of the string in the URL will be assigned to the context params and functions below this point in the hierarchy will have access to the new key.
---🐱💻 Bellow an example that visits "/category/suspense?page=2":
-
import Nullstack from 'nullstack';
-
-class Books extends Nullstack {
-
- async initiate({params}) {
- const page = parseInt(params.page) || 1;
- const category = params.slug;
- this.books = await this.getBooks({category, page});
- }
-
-}
-
-export default Books;
-
-import Nullstack from 'nullstack';
-import Books from './Books';
-
-class Application extends Nullstack {
-
- render() {
- <main>
- <Books route="/category/:slug">
- </main>
- }
-
-}
-
-export default Application;
-
-When a dynamic segment is changed, as for example moving from "/category/suspense" to "/category/comedy", the component will be terminated and a new instance will be created.
-Changing a query param will not re-instantiate the component.
-Children of the component will not be re-instantiated automatically, you can set the same route to the children or do it manually if you desire this behavior.
---💡 The behavior mentioned above solves many of the problems you have to normally deal with manually.
-
Wildcards are routes declared with "*" as the attribute value.
-These routes will match anything if nothing above it matches the requested URL.
-import Nullstack from 'nullstack';
-import Home from './Home';
-
-class Application extends Nullstack {
-
- render({count}) {
- return (
- <main>
- <Home route="/" />
- <div route="*"> Wildcard </abbr>
- </main>
- )
- }
-
-}
-
-Wildcards can be prefixed with a segment.
---✨ this is especially useful for engines that can be mounted in your application.
-
import Nullstack from 'nullstack';
-import Home from './Home';
-import BlogEngine from './BlogEngine';
-
-class Application extends Nullstack {
-
- render({count}) {
- return (
- <main>
- <Home route="/" />
- <BlogEngine route="/blog/*" />
- </main>
- )
- }
-
-}
-
-The router key is an object proxy injected into every client instance.
-The router has two keys:
-The url key returns everything after the domain including the path and the query params as a string.
-The path key returns only the path without query params.
---💡 Both keys above automatically remove the trailing slash for convenience.
-
Assigning to url or path will cause a redirect.
---💡 Under the hood a tags and params use the router.
-
import Nullstack from 'nullstack';
-
-class Application extends Nullstack {
-
- prepare({router}) {
- if(router.path == '/') {
- router.path = '/dashboard';
- }
- }
-
-}
-
-Updating router.url or router.path will raise a custom event.
-import Nullstack from 'nullstack';
-
-class Analytics extends Nullstack {
-
- hydrate({router}) {
- window.addEventListener(router.event, () => {
- console.log(router.url);
- });
- }
-
-}
-
-export default Analytics;
-
-Anchor tags accept some convenient special attributes besides the regular href.
-You can set the params attribute with an object as the value.
-The path will remain the same as the current router path, but the params will be replaced by the new params you specify.
-<a params={{page: 1}}> First Page </a>
-
-If you wish to just update some params and keep the others, you can use the javascript spread operator for that.
-<a params={{...params, page: 1}}> First Page </a>
-
-You can set the path attribute with a string starting with "/" and no query params.
-The params will remain the same, but the path will be updated.
-<a path="/category/suspense"> Suspense Books </a>
-
-Both attributes above can be used at the same time.
-<a path="/category/suspense" params={{...params, page: 1}}> Suspense Books </a>
-
-The first route to be matched will be rendered.
-The other elements with a route will not be rendered, however, elements on the same level without a route attribute will render normally.
-The router will lookup for one route per dom depth level, this allows you to have nested routing behavior.
-import Nullstack from 'nullstack';
-import Home from './Home';
-
-class Application extends Nullstack {
-
- renderPage() {
- return (
- <section>
- <div route="/page/about"> About Page </div>
- <div route="/page/contact"> Contact Page </div>
- </section>
- )
- }
-
- render({count}) {
- return (
- <main>
- <Home route="/" />
- <Page route="/page/:slug">
- </main>
- )
- }
-
-}
-
-export default Application;
-
-⚔ Learn about two-way bindings.
-Nullstack has built-in routes, it would make no sense otherwise since web applications are expected to have hyperlinks.
\nAny tag can receive a route attribute, be it a component, inner component, or simple HTML tag.
\nimport Nullstack from 'nullstack';\nimport Page from './Page';\n\nclass Application extends Nullstack {\n\n renderHome() {\n return (\n <section> Home </section>\n )\n }\n \n render({count}) {\n return (\n <main>\n <Home route=\"/\" />\n <Page route=\"/page\">\n <abbr route=\"/abbreviations\"> Abbreviations </abbr>\n </main>\n )\n }\n\n}\n\nexport default Application;\n\nLinks on Nullstack are simple a tags with the href value starting with "/".
\n<a href=\"/page/about\"> About Page </a>\n\n\n\n💡 On the client side the click event will push history without reloading the page.
\n
\n\n✨ You can still assign your own click event to the tag without losing the framework behavior.
\n
The params key is an object proxy injected into every client instance.
\nEach query string param is mapped to this object.
\nBy default any key you request from this object will return a string.
\nIf the value is undefined it will return an empty string.
\nIf the value is true or false it will return a boolean instead.
\n\n\n🐱💻 Bellow an exemple that visits "/books?expanded=true&page=2":
\n
import Nullstack from 'nullstack';\n\nclass Books extends Nullstack {\n\n async initiate({params}) {\n if(params.expanded) {\n const page = parseInt(params.page) || 1;\n this.books = await this.getBooks({page});\n }\n }\n\n}\n\nexport default Books;\n\nAssigning to a params key will cause a redirect to the route with updated params.
\nWhen you assign to a param, the value will be converted to JSON before being set.
\n\n\n💡 Redirects work in batches, so there is no performance loss in multiple assignments.
\n
import Nullstack from 'nullstack';\n\nclass Paginator extends Nullstack {\n\n handleClick({params}) {\n params.filter = '';\n params.page = 1;\n }\n\n}\n\nexport default Paginator;\n\nAssigning an empty string to a param will remove it from the url.
\nPart of the route can be an expression started with ":" followed by a param name.
\nThis value will be matched against any string in the same directory position.
\nThe value of the string in the URL will be assigned to the context params and functions below this point in the hierarchy will have access to the new key.
\n\n\n🐱💻 Bellow an example that visits "/category/suspense?page=2":
\n
import Nullstack from 'nullstack';\n\nclass Books extends Nullstack {\n\n async initiate({params}) {\n const page = parseInt(params.page) || 1;\n const category = params.slug;\n this.books = await this.getBooks({category, page});\n }\n\n}\n\nexport default Books;\n\nimport Nullstack from 'nullstack';\nimport Books from './Books';\n\nclass Application extends Nullstack {\n\n render() {\n <main>\n <Books route=\"/category/:slug\">\n </main>\n }\n\n}\n\nexport default Application;\n\nWhen a dynamic segment is changed, as for example moving from "/category/suspense" to "/category/comedy", the component will be terminated and a new instance will be created.
\nChanging a query param will not re-instantiate the component.
\nChildren of the component will not be re-instantiated automatically, you can set the same route to the children or do it manually if you desire this behavior.
\n\n\n💡 The behavior mentioned above solves many of the problems you have to normally deal with manually.
\n
Wildcards are routes declared with "*" as the attribute value.
\nThese routes will match anything if nothing above it matches the requested URL.
\nimport Nullstack from 'nullstack';\nimport Home from './Home';\n\nclass Application extends Nullstack {\n\n render({count}) {\n return (\n <main>\n <Home route=\"/\" />\n <div route=\"*\"> Wildcard </abbr>\n </main>\n )\n }\n\n}\n\nWildcards can be prefixed with a segment.
\n\n\n✨ this is especially useful for engines that can be mounted in your application.
\n
import Nullstack from 'nullstack';\nimport Home from './Home';\nimport BlogEngine from './BlogEngine';\n\nclass Application extends Nullstack {\n\n render({count}) {\n return (\n <main>\n <Home route=\"/\" />\n <BlogEngine route=\"/blog/*\" />\n </main>\n )\n }\n\n}\n\nThe router key is an object proxy injected into every client instance.
\nThe router has two keys:
\nThe url key returns everything after the domain including the path and the query params as a string.
\nThe path key returns only the path without query params.
\n\n\n💡 Both keys above automatically remove the trailing slash for convenience.
\n
Assigning to url or path will cause a redirect.
\n\n\n💡 Under the hood a tags and params use the router.
\n
import Nullstack from 'nullstack';\n\nclass Application extends Nullstack {\n\n prepare({router}) {\n if(router.path == '/') {\n router.path = '/dashboard';\n }\n }\n\n}\n\nUpdating router.url or router.path will raise a custom event.
\nimport Nullstack from 'nullstack';\n\nclass Analytics extends Nullstack {\n\n hydrate({router}) {\n window.addEventListener(router.event, () => {\n console.log(router.url);\n });\n }\n\n}\n\nexport default Analytics;\n\nAnchor tags accept some convenient special attributes besides the regular href.
\nYou can set the params attribute with an object as the value.
\nThe path will remain the same as the current router path, but the params will be replaced by the new params you specify.
\n<a params={{page: 1}}> First Page </a>\n\nIf you wish to just update some params and keep the others, you can use the javascript spread operator for that.
\n<a params={{...params, page: 1}}> First Page </a>\n\nYou can set the path attribute with a string starting with "/" and no query params.
\nThe params will remain the same, but the path will be updated.
\n<a path=\"/category/suspense\"> Suspense Books </a>\n\nBoth attributes above can be used at the same time.
\n<a path=\"/category/suspense\" params={{...params, page: 1}}> Suspense Books </a>\n\nThe first route to be matched will be rendered.
\nThe other elements with a route will not be rendered, however, elements on the same level without a route attribute will render normally.
\nThe router will lookup for one route per dom depth level, this allows you to have nested routing behavior.
\nimport Nullstack from 'nullstack';\nimport Home from './Home';\n\nclass Application extends Nullstack {\n\n renderPage() {\n return (\n <section>\n <div route=\"/page/about\"> About Page </div>\n <div route=\"/page/contact\"> Contact Page </div>\n </section>\n )\n }\n \n render({count}) {\n return (\n <main>\n <Home route=\"/\" />\n <Page route=\"/page/:slug\">\n </main>\n )\n }\n\n}\n\nexport default Application;\n\n⚔ Learn about two-way bindings.
\n","description":"Nullstack has built-in routes, it would make no sense otherwise since web applications are expected to have hyperlinks."},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Routes and Params - Nullstack","description":"Nullstack has built-in routes, it would make no sense otherwise since web applications are expected to have hyperlinks."}} \ No newline at end of file diff --git a/docs/server-functions/index.html b/docs/server-functions/index.html deleted file mode 100644 index 4c84f72d..00000000 --- a/docs/server-functions/index.html +++ /dev/null @@ -1,202 +0,0 @@ - - - - - - -Server functions are specialized microservices that at transpile time are converted into API entry points.
-To flag a function as a server function, you must declare it as static async.
-Being a static function means it has no access to the instance scope.
-However, instead of calling the static version from the class, you must invoke it as an instance function.
-Server functions can be called anytime in your code and are not limited to prerender steps.
-import Nullstack from 'nullstack';
-
-class Component extends Nullstack {
-
- static async increment(context) {
- context.count++;
- }
-
- async handleClick() {
- await this.increment();
- }
-
- // ...
-
-}
-
-export default Component;
-
---✨ Learn more about the server context.
-
When you call a server function from the client, the arguments will be serialized as JSON.
-The arguments will be posted against the automatically generated API and merged with the server context when it reaches the server.
-The return value of the server function will be serialized back to the client and can be seamlessly used as if it were a local function.
-import Nullstack from 'nullstack';
-
-class Component extends Nullstack {
-
- static async increment(context) {
- context.count++;
- return context.count;
- }
-
- async handleClick() {
- this.count = await this.increment();
- }
-
- // ...
-
-}
-
-export default Component;
-
-Server functions will be used as local functions, simply aliasing the instance call to the class and merging the arguments with the server context
-Dates are serialized as UTC in JSON and deserialized back to date objects.
-import Nullstack from 'nullstack';
-
-class Component extends Nullstack {
-
- async initiate() {
- const date = new Date();
- const verified = this.verifyDay({date});
- }
-
- static async verifyDay({date}) {
- return date.getDay() === new Date().getDay();
- }
-
- // ...
-
-}
-
-export default Component;
-
-Fetch is available in both server and client functions for the sake of isomorphy.
-import Nullstack from 'nullstack';
-
-class Component extends Nullstack {
-
- // ...
-
- async initiate() {
- const url = 'https://api.github.com/repos/nullstack/nullstack/issues';
- const response = await fetch(url);
- this.issues = await response.json();
- }
-
- // ...
-
-}
-
-export default Component;
-
-Imported dependencies that are only used inside server functions will be excluded from the client bundle.
-This is useful for both accessing node.js exclusive modules and reducing the client bundle size by preprocessing data like markdown without having to expose the dependency to the end-user.
-import Nullstack from 'nullstack';
-import {readFileSync} from 'fs';
-import {Remarkable} from 'remarkable';
-
-class Application extends Nullstack {
-
- static async getTasks() {
- const readme = readFileSync('README.md', 'utf-8');
- return new Remarkable().render(readme);
- }
-
- // ...
-
-}
-
-export default Application;
-
-Keep in mind that every server function is similar to an express route in API and must be coded without depending on view logic for security.
---🔒 Server functions with the name starting with "start" (and optionally followed by an uppercase letter) do not generate an API endpoint to avoid malicious context flooding.
-
import Nullstack from 'nullstack';
-
-class Component extends Nullstack {
-
- static async getCount({request, count}) {
- if(!request.session.user) return 0;
- return count;
- }
-
- // ...
-
-}
-
-export default Component;
-
---💡 Server functions are not exposed to the client.
-
--✨ Learn more about the NJS file extension.
-
Server function names cannot collide with instance method names from the current class or its parent classes.
-The following words cannot be used in server functions:
-Server functions named start will not generate an API endpoint and can only be called by other server functions.
-Automatically generated API endpoints are not meant to be used by 3rd-party apps.
-The URL and implementation may change between versions of Nullstack.
---✨ If you want to build an API, learn more about how to create an API with Nullstack.
-
⚔ Learn about the context.
-Server functions are specialized microservices that at transpile time are converted into API entry points.
\nTo flag a function as a server function, you must declare it as static async.
\nBeing a static function means it has no access to the instance scope.
\nHowever, instead of calling the static version from the class, you must invoke it as an instance function.
\nServer functions can be called anytime in your code and are not limited to prerender steps.
\nimport Nullstack from 'nullstack';\n\nclass Component extends Nullstack {\n\n static async increment(context) {\n context.count++;\n }\n\n async handleClick() {\n await this.increment();\n }\n\n // ...\n\n}\n\nexport default Component;\n\n\n\n✨ Learn more about the server context.
\n
When you call a server function from the client, the arguments will be serialized as JSON.
\nThe arguments will be posted against the automatically generated API and merged with the server context when it reaches the server.
\nThe return value of the server function will be serialized back to the client and can be seamlessly used as if it were a local function.
\nimport Nullstack from 'nullstack';\n\nclass Component extends Nullstack {\n\n static async increment(context) {\n context.count++;\n return context.count;\n }\n\n async handleClick() {\n this.count = await this.increment();\n }\n\n // ...\n\n}\n\nexport default Component;\n\nServer functions will be used as local functions, simply aliasing the instance call to the class and merging the arguments with the server context
\nDates are serialized as UTC in JSON and deserialized back to date objects.
\nimport Nullstack from 'nullstack';\n\nclass Component extends Nullstack {\n\n async initiate() {\n const date = new Date();\n const verified = this.verifyDay({date});\n }\n\n static async verifyDay({date}) {\n return date.getDay() === new Date().getDay();\n }\n\n // ...\n\n}\n\nexport default Component;\n\nFetch is available in both server and client functions for the sake of isomorphy.
\nimport Nullstack from 'nullstack';\n\nclass Component extends Nullstack {\n\n // ...\n\n async initiate() {\n const url = 'https://api.github.com/repos/nullstack/nullstack/issues';\n const response = await fetch(url);\n this.issues = await response.json();\n }\n\n // ...\n\n}\n\nexport default Component;\n\nImported dependencies that are only used inside server functions will be excluded from the client bundle.
\nThis is useful for both accessing node.js exclusive modules and reducing the client bundle size by preprocessing data like markdown without having to expose the dependency to the end-user.
\nimport Nullstack from 'nullstack';\nimport {readFileSync} from 'fs';\nimport {Remarkable} from 'remarkable';\n\nclass Application extends Nullstack {\n\n static async getTasks() {\n const readme = readFileSync('README.md', 'utf-8');\n return new Remarkable().render(readme);\n }\n\n // ...\n\n}\n\nexport default Application;\n\nKeep in mind that every server function is similar to an express route in API and must be coded without depending on view logic for security.
\n\n\n🔒 Server functions with the name starting with "start" (and optionally followed by an uppercase letter) do not generate an API endpoint to avoid malicious context flooding.
\n
import Nullstack from 'nullstack';\n\nclass Component extends Nullstack {\n\n static async getCount({request, count}) {\n if(!request.session.user) return 0;\n return count;\n }\n\n // ...\n\n}\n\nexport default Component;\n\n\n\n💡 Server functions are not exposed to the client.
\n
\n\n✨ Learn more about the NJS file extension.
\n
Server function names cannot collide with instance method names from the current class or its parent classes.
\nThe following words cannot be used in server functions:
\nServer functions named start will not generate an API endpoint and can only be called by other server functions.
\nAutomatically generated API endpoints are not meant to be used by 3rd-party apps.
\nThe URL and implementation may change between versions of Nullstack.
\n\n\n✨ If you want to build an API, learn more about how to create an API with Nullstack.
\n
⚔ Learn about the context.
\n","description":"Server functions are specialized microservices that at transpile time are converted into API entry points"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Server Functions - Nullstack","description":"Server functions are specialized microservices that at transpile time are converted into API entry points"}} \ No newline at end of file diff --git a/docs/server-request-and-response/index.html b/docs/server-request-and-response/index.html deleted file mode 100644 index 2af50aad..00000000 --- a/docs/server-request-and-response/index.html +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - -The server key is a proxy around the Express instance that runs Nullstack under the hood.
-The server object is present only in the server context.
-The following functions are tunneled back to the express server:
---✨ If you wanna know how to make an API with Nullstack, this is the way.
-
import Nullstack from 'nullstack';
-
-class Application extends Nullstack {
-
- static async start({server}) {
- server.get('/api/books', (request, response) => {
- response.json({books: []});
- });
- }
-
- // ...
-
-}
-
-export default Application;
-
-Other available keys are:
-import Nullstack from 'nullstack';
-
-class Application extends Nullstack {
-
- static async start({server}) {
- server.port = 3000;
- server.maximumPayloadSize = '5mb';
- server.cors = {
- origin: 'http://localhost:6969',
- optionsSuccessStatus: 200
- }
- }
-
- // ...
-
-}
-
-export default Application;
-
-The cors object will be passed as the argument to express cors plugin
-Every server function context is merged with the original request and response objects from express.
-If you raise a response manually it will override the framework's server-side rendering response.
-import Nullstack from 'nullstack';
-
-class Application extends Nullstack {
-
- static async getBooks({request, response}) {
- if(!request.session.user) {
- response.status(401).json({unauthorized: true});
- }
- }
-
- // ...
-
-}
-
-export default Application;
-
-⚔ Learn about styles.
-The server key is a proxy around the Express instance that runs Nullstack under the hood.
\nThe server object is present only in the server context.
\nThe following functions are tunneled back to the express server:
\n\n\n✨ If you wanna know how to make an API with Nullstack, this is the way.
\n
import Nullstack from 'nullstack';\n\nclass Application extends Nullstack {\n\n static async start({server}) {\n server.get('/api/books', (request, response) => {\n response.json({books: []});\n });\n }\n\n // ...\n\n}\n\nexport default Application;\n\nOther available keys are:
\nimport Nullstack from 'nullstack';\n\nclass Application extends Nullstack {\n\n static async start({server}) {\n server.port = 3000;\n server.maximumPayloadSize = '5mb';\n server.cors = {\n origin: 'http://localhost:6969',\n optionsSuccessStatus: 200\n }\n }\n\n // ...\n\n}\n\nexport default Application;\n\nThe cors object will be passed as the argument to express cors plugin
\nEvery server function context is merged with the original request and response objects from express.
\nIf you raise a response manually it will override the framework's server-side rendering response.
\nimport Nullstack from 'nullstack';\n\nclass Application extends Nullstack {\n\n static async getBooks({request, response}) {\n if(!request.session.user) {\n response.status(401).json({unauthorized: true});\n }\n }\n\n // ...\n\n}\n\nexport default Application;\n\n⚔ Learn about styles.
\n","description":"The server key is a proxy around the express instance that runs Nullstack under the hood"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Server request and response - Nullstack","description":"The server key is a proxy around the express instance that runs Nullstack under the hood"}} \ No newline at end of file diff --git a/docs/server-side-rendering/index.html b/docs/server-side-rendering/index.html deleted file mode 100644 index acea9f19..00000000 --- a/docs/server-side-rendering/index.html +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - -Nullstack optimizes SEO and response times out of the box by generating HTML for the route that you enter the application from.
-Server-side rendering is good for SEO since it gives as fast as possible crawlable markup for search engines.
-Nullstack starts the application for the user by first serving HTML of only the requested page with no overhead.
-Before serving the HTML, Nullstack will wait for prepare and initiate of all components of that route to be resolved.
-While server-side rendering all server functions run locally without the need to fetch an API, making the process even faster.
-After the document is already painted in the browser, Nullstack loads the javascript client bundle and starts the hydration process.
-No further requests to the server are made to recover the application state during hydration.
-The page head will generate the necessary meta tags for SEO based on the contents of the project and page context keys.
-⚔ Learn about static site generation.
-Nullstack optimizes SEO and response times out of the box by generating HTML for the route that you enter the application from.
\nServer-side rendering is good for SEO since it gives as fast as possible crawlable markup for search engines.
\nNullstack starts the application for the user by first serving HTML of only the requested page with no overhead.
\nBefore serving the HTML, Nullstack will wait for prepare and initiate of all components of that route to be resolved.
\nWhile server-side rendering all server functions run locally without the need to fetch an API, making the process even faster.
\nAfter the document is already painted in the browser, Nullstack loads the javascript client bundle and starts the hydration process.
\nNo further requests to the server are made to recover the application state during hydration.
\nThe page head will generate the necessary meta tags for SEO based on the contents of the project and page context keys.
\n⚔ Learn about static site generation.
\n","description":"Nullstack optimizes SEO and response times out of the box by generating HTML for the route that you enter the application from"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Server-Side Rendering - Nullstack","description":"Nullstack optimizes SEO and response times out of the box by generating HTML for the route that you enter the application from"}} \ No newline at end of file diff --git a/docs/service-worker-f0cee0769c64977a287dddeaae84c7f6.js b/docs/service-worker-f0cee0769c64977a287dddeaae84c7f6.js deleted file mode 100644 index 3edf57d6..00000000 --- a/docs/service-worker-f0cee0769c64977a287dddeaae84c7f6.js +++ /dev/null @@ -1,235 +0,0 @@ -self.context = { - "environment": { - "client": false, - "server": true, - "development": false, - "production": true, - "static": true, - "key": "f0cee0769c64977a287dddeaae84c7f6" - }, - "project": { - "type": "website", - "display": "standalone", - "orientation": "portrait", - "scope": "/", - "root": "/", - "favicon": "/favicon-96x96.png", - "icons": { - "72": "/icon-72x72.png", - "96": "/icon-96x96.png", - "128": "/icon-128x128.png", - "144": "/icon-144x144.png", - "152": "/icon-152x152.png", - "180": "/icon-180x180.png", - "192": "/icon-192x192.png", - "384": "/icon-384x384.png", - "512": "/icon-512x512.png" - }, - "disallow": [], - "sitemap": true, - "cdn": "", - "protocol": "https", - "name": "Nullstack", - "domain": "nullstack.app", - "color": "#d22365", - "backgroundColor": "#2d3748" - }, - "settings": {}, - "worker": { - "enabled": true, - "fetching": false, - "preload": [ - "/nullstack.svg", - "/about", - "/application-startup", - "/context-data", - "/context-environment", - "/context-page", - "/context-project", - "/context-secrets", - "/context-settings", - "/context", - "/full-stack-lifecycle", - "/getting-started", - "/how-to-deploy-a-nullstack-application", - "/how-to-use-facebook-pixel-with-nullstack", - "/how-to-use-google-analytics-with-nullstack", - "/how-to-use-mongodb-with-nullstack", - "/instance-key", - "/instance-self", - "/njs-file-extension", - "/renderable-components", - "/routes-and-params", - "/server-functions", - "/server-request-and-response", - "/server-side-rendering", - "/service-worker", - "/stateful-components", - "/static-site-generation", - "/styles", - "/two-way-bindings", - "/documentation", - "/components", - "/about", - "/contributors", - "/roboto-v20-latin-300.woff2", - "/roboto-v20-latin-500.woff2", - "/crete-round-v9-latin-regular.woff2", - "/nullachan.png" - ], - "headers": {}, - "loading": {} - } -}; - -async function load(event) { - const response = await event.preloadResponse; - if (response) return response; - return await fetch(event.request); -} - -function toAPI(url) { - let [path, query] = url.split('?'); - if(path.indexOf('.') === -1) { - path += '/index.json'; - } - return query ? `${path}?${query}` : path; -} - -async function extractData(response) { - const html = await response.clone().text(); - const instancesLookup = 'window.instances = '; - const instances = html.split("\n").find((line) => line.indexOf(instancesLookup) > -1).split(instancesLookup)[1].slice(0, -1); - const pageLookup = 'window.page = '; - const page = html.split("\n").find((line) => line.indexOf(pageLookup) > -1).split(pageLookup)[1].slice(0, -1); - const json = `{"instances": ${instances}, "page": ${page}}`; - return new Response(json, { - headers: {'Content-Type': 'application/json'} - }); -} - -async function injectData(templateResponse, cachedDataResponse) { - const data = await cachedDataResponse.json(); - const input = await templateResponse.text(); - const output = input.split(`\n`).map((line) => { - if(line.indexOf('The worker is a proxy in the framework store part of your context and gives you granular control of your PWA behavior.
-This key is readwrite in the server context.
-This key is readonly in the client context.
-Worker keys will be used to generate the service worker file and should be set during the application startup.
-Worker keys are frozen after the application startup.
-The following keys are available in the object during the startup:
-The enabled key defines if the service worker will be automatically registered by Nullstack.
-By default enabled is set to true on production mode and false on development mode.
-Preload is an array of paths that will be cached when the service worker is installed.
-The assets required to start the application will be preloaded automatically, and you should configure only the extra pages you want to have available offline.
-import Nullstack from 'nullstack';
-import path from 'path';
-import {readdirSync} from 'fs';
-
-class Application extends Nullstack {
-
- static async start({worker}) {
- const articles = readdirSync(path.join(__dirname, '..', 'articles'));
- worker.preload = [
- ...articles.map((article) => '/' + article.replace('.md', '')),
- '/nullstack.svg',
- '/documentation',
- '/components'
- ]
- }
-
- // ...
-
-}
-
-export default Application;
-
---💡 the example above is extracted from this repository and allows the documentation to be fully accessible offline.
-
The following keys are available as readonly in the client context:
-The following keys are available as readwrite in the client context:
-The responsive key determines if the application has all the responses it needs to render the current page.
-Nullstack will try to keep your application responsive as long as possible and set the key to false only when there are no ways of retrieving any response from the network or offline according to the fetch strategy for the environment.
-The online key will listen for network events and rerender the application when navigator.onLine value changes.
-When the application is back online Nullstack will try to make the application responsive again and rerender if necessary.
-import Nullstack from 'nullstack';
-// ...
-
-class Application extends Nullstack {
-
- // ...
-
- render({worker}) {
- if(!worker.responsive) {
- return <OfflineWarning />
- }
- return (
- <main>
- <Home route="/" />
- </main>
- )
- }
-
-}
-
-You can access the current service worker registration and installation from the worker key to control the flow of your PWA.
-The registration key refers to the service worker registration and will be only available once the registration process is complete.
-The installation key refers to the deferred installation prompt event and will only be available if the beforeinstallprompt event is triggered.
-import Nullstack from 'nullstack';
-
-class PWAInstaller extends Nullstack {
-
- installed = false;
- hidden = false;
-
- async prompt({worker}) {
- try {
- worker.installation.prompt();
- const {outcome} = await worker.installation.userChoice;
- if (outcome === 'accepted') {
- console.log('User accepted the A2HS prompt');
- } else {
- console.log('User dismissed the A2HS prompt');
- }
- } finally {
- this.hidden = true;
- }
- }
-
- render({worker, project}) {
- if(this.hidden) return false;
- if(!worker.installation) return false;
- return (
- <div>
- <img src={project.favicon} />
- <p> Do you want to add {project.name} to your home screen?</p>
- <button onclick={this.prompt}> Install </button>
- <button onclick={{hidden: true}}> Not Now </button>
- </div>
- )
- }
-
-}
-
-export default PWAInstaller;
-
-When a server function is called fetching will be set to true until the response is resolved.
-When a server function is called a key with the name of the server function invoked will be set to true in the loading key until the response is resolved.
-Any key you invoke on the loading object will always return a boolean instead of undefined for consistency.
-When the server is emulating the client context for server-side rendering, every key of the loading object will always return false, saving multiple render cycles in performance.
-import Nullstack from 'nullstack';
-
-class Page extends Nullstack {
-
- static async save() {
- // ...
- }
-
- async submit() {
- await this.save();
- }
-
- render({worker}) {
- return (
- <form onsubmit={this.save}>
- {worker.fetching &&
- <span> loading... </span>
- }
- <button disabled={worker.loading.save}>
- Save
- </button>
- </form>
- )
- }
-
-}
-
-export default Page;
-
-You can use the headers key to configure the headers that the worker will use when fetching a server function.
---🔥 Headers will be ignored when a server function is called during the server-side rendering process.
-
import Nullstack from 'nullstack';
-
-class LoginPage extends Nullstack {
-
- // ...
-
- async submit({worker}) {
- // ...
- this.headers['Authorization'] = `Bearer ${token}`;
- // ...
- }
-
- static async authorize({request}) {
- const authorization = request.headers['Authorization'];
- // ...
- }
-
- // ...
-
-}
-
-
-export default LoginPage;
-
---✨ Learn more about the server request and response
-
Nullstack will install automatically your service worker if enabled is set to true with the following events:
-You can override any of those events by creating a service-worker.js in the public folder;
-If any of the keywords above are found Nullstack will inject your function in the service worker code instead of the default.
-For convenience a context key is injected in the service worker self with the following keys:
-function activate(event) {
- event.waitUntil(async function() {
- const cacheNames = await caches.keys();
- const cachesToDelete = cacheNames.filter(cacheName => cacheName !== self.context.environment.key);
- await Promise.all(cachesToDelete.map((cacheName) => caches.delete(cacheName)));
- if (self.registration.navigationPreload) {
- await self.registration.navigationPreload.enable();
- }
- self.clients.claim();
- }());
-}
-
-self.addEventListener('activate', activate);
-
---💡 The example above is extracted from the generated service worker and uses self.context.environment.key
-
The worker is a proxy in the framework store part of your context and gives you granular control of your PWA behavior.
\nThis key is readwrite in the server context.
\nThis key is readonly in the client context.
\nWorker keys will be used to generate the service worker file and should be set during the application startup.
\nWorker keys are frozen after the application startup.
\nThe following keys are available in the object during the startup:
\nThe enabled key defines if the service worker will be automatically registered by Nullstack.
\nBy default enabled is set to true on production mode and false on development mode.
\nPreload is an array of paths that will be cached when the service worker is installed.
\nThe assets required to start the application will be preloaded automatically, and you should configure only the extra pages you want to have available offline.
\nimport Nullstack from 'nullstack';\nimport path from 'path';\nimport {readdirSync} from 'fs';\n\nclass Application extends Nullstack {\n\n static async start({worker}) {\n const articles = readdirSync(path.join(__dirname, '..', 'articles'));\n worker.preload = [\n ...articles.map((article) => '/' + article.replace('.md', '')),\n '/nullstack.svg',\n '/documentation',\n '/components'\n ]\n }\n \n // ...\n\n}\n\nexport default Application;\n\n\n\n💡 the example above is extracted from this repository and allows the documentation to be fully accessible offline.
\n
The following keys are available as readonly in the client context:
\nThe following keys are available as readwrite in the client context:
\nThe responsive key determines if the application has all the responses it needs to render the current page.
\nNullstack will try to keep your application responsive as long as possible and set the key to false only when there are no ways of retrieving any response from the network or offline according to the fetch strategy for the environment.
\nThe online key will listen for network events and rerender the application when navigator.onLine value changes.
\nWhen the application is back online Nullstack will try to make the application responsive again and rerender if necessary.
\nimport Nullstack from 'nullstack';\n// ...\n\nclass Application extends Nullstack {\n \n // ...\n\n render({worker}) {\n if(!worker.responsive) {\n return <OfflineWarning />\n }\n return (\n <main>\n <Home route=\"/\" />\n </main>\n )\n }\n\n}\n\nYou can access the current service worker registration and installation from the worker key to control the flow of your PWA.
\nThe registration key refers to the service worker registration and will be only available once the registration process is complete.
\nThe installation key refers to the deferred installation prompt event and will only be available if the beforeinstallprompt event is triggered.
\nimport Nullstack from 'nullstack';\n\nclass PWAInstaller extends Nullstack {\n\n installed = false;\n hidden = false;\n\n async prompt({worker}) {\n try {\n worker.installation.prompt();\n const {outcome} = await worker.installation.userChoice;\n if (outcome === 'accepted') {\n console.log('User accepted the A2HS prompt');\n } else {\n console.log('User dismissed the A2HS prompt');\n }\n } finally {\n this.hidden = true;\n }\n }\n \n render({worker, project}) {\n if(this.hidden) return false;\n if(!worker.installation) return false;\n return (\n <div>\n <img src={project.favicon} />\n <p> Do you want to add {project.name} to your home screen?</p>\n <button onclick={this.prompt}> Install </button>\n <button onclick={{hidden: true}}> Not Now </button>\n </div>\n )\n }\n\n}\n\nexport default PWAInstaller;\n\nWhen a server function is called fetching will be set to true until the response is resolved.
\nWhen a server function is called a key with the name of the server function invoked will be set to true in the loading key until the response is resolved.
\nAny key you invoke on the loading object will always return a boolean instead of undefined for consistency.
\nWhen the server is emulating the client context for server-side rendering, every key of the loading object will always return false, saving multiple render cycles in performance.
\nimport Nullstack from 'nullstack';\n\nclass Page extends Nullstack {\n\n static async save() {\n // ...\n }\n\n async submit() {\n await this.save();\n }\n \n render({worker}) {\n return (\n <form onsubmit={this.save}> \n {worker.fetching && \n <span> loading... </span>\n }\n <button disabled={worker.loading.save}> \n Save\n </button>\n </form>\n )\n }\n\n}\n\nexport default Page;\n\nYou can use the headers key to configure the headers that the worker will use when fetching a server function.
\n\n\n🔥 Headers will be ignored when a server function is called during the server-side rendering process.
\n
import Nullstack from 'nullstack';\n\nclass LoginPage extends Nullstack {\n\n // ...\n\n async submit({worker}) {\n // ...\n this.headers['Authorization'] = `Bearer ${token}`;\n // ...\n }\n\n static async authorize({request}) {\n const authorization = request.headers['Authorization'];\n // ...\n }\n \n // ...\n\n}\n\n\nexport default LoginPage;\n\n\n\n✨ Learn more about the server request and response
\n
Nullstack will install automatically your service worker if enabled is set to true with the following events:
\nYou can override any of those events by creating a service-worker.js in the public folder;
\nIf any of the keywords above are found Nullstack will inject your function in the service worker code instead of the default.
\nFor convenience a context key is injected in the service worker self with the following keys:
\nfunction activate(event) {\n event.waitUntil(async function() {\n const cacheNames = await caches.keys();\n const cachesToDelete = cacheNames.filter(cacheName => cacheName !== self.context.environment.key);\n await Promise.all(cachesToDelete.map((cacheName) => caches.delete(cacheName)));\n if (self.registration.navigationPreload) {\n await self.registration.navigationPreload.enable();\n }\n self.clients.claim();\n }());\n}\n\nself.addEventListener('activate', activate);\n\n\n\n💡 The example above is extracted from the generated service worker and uses self.context.environment.key
\n
⚔ Learn how to deploy a Nullstack application.
\n","description":"The worker is a proxy in the framework store part of your context and gives you granular control of your PWA behavior."},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Context Service Worker - Nullstack","description":"The worker is a proxy in the framework store part of your context and gives you granular control of your PWA behavior."}} \ No newline at end of file diff --git a/docs/sitemap.xml b/docs/sitemap.xml deleted file mode 100644 index c0346aa3..00000000 --- a/docs/sitemap.xml +++ /dev/null @@ -1 +0,0 @@ -A productive full-stack web framework should not force you to think about framework details.
-Nullstack takes control of its subclasses and generates a proxy for each instance.
-When you call anything on your class you are actually telling Nullstack what to do with the environment behind the scenes.
-This allows you to use vanilla javascript operations like assigning to a variable and see the reflection in the dom.
-You can mutate instance variables to update your application state.
-Functions are automatically bound to the instance proxy and can be passed as a reference to events.
-Events are declared like normal HTML attributes.
-import Nullstack from 'nullstack';
-
-class Counter extends Nullstack {
-
- count = 0;
-
- increment() {
- this.count++;
- }
-
- render() {
- return (
- <button onclick={this.increment}>
- {this.count}
- </button>
- )
- }
-
-}
-
-export default Counter;
-
---💡 Updates are made in batches, usually while awaiting async calls, so making multiple assignments have no performance costs!
-
You can shortcut events that are simple assignments by passing an object to the event.
-Each key of the object will be assigned to the instance.
-import Nullstack from 'nullstack';
-
-class Counter extends Nullstack {
-
- count = 0;
-
- render() {
- return (
- <button onclick={{count: this.count + 1}}>
- {this.count}
- </button>
- )
- }
-
-}
-
-export default Counter;
-
-By default, events refer to this when you pass an object.
-You can use the source attribute to define which object will receive the assignments.
-import Nullstack from 'nullstack';
-
-class Paginator extends Nullstack {
-
- render({params}) {
- return (
- <button source={params} onclick={{page: 1}}>
- First Page
- </button>
- )
- }
-
-}
-
-export default Paginator;
-
---✨ Learn more about context params.
-
--💡 If you do not declare a source to the event, Nullstack will inject a source={this} at transpile time in order to completely skip the runtime lookup process!
-
Attributes of the event target will be merged to the instance context and can be destructured in the function signature.
-import Nullstack from 'nullstack';
-
-class Counter extends Nullstack {
-
- count = 0;
-
- increment({delta}) {
- this.count += delta;
- }
-
- render() {
- return (
- <button onclick={this.increment} delta={2}>
- {this.count}
- </button>
- )
- }
-
-}
-
-export default Counter;
-
---💡 Any attribute with primitive value will be added to the DOM.
-
--✨ Consider using data attributes to make your html valid.
-
The browser default behavior is prevented by default.
-You can opt-out of this by declaring a default attribute to the event element.
-A reference to the original event is always merged with the function context.
-import Nullstack from 'nullstack';
-
-class Form extends Nullstack {
-
- submit({event}) {
- event.preventDefault();
- }
-
- render() {
- return (
- <form onsubmit={this.submit} default>
- <button> Submit </button>
- </form>
- )
- }
-
-}
-
-export default Form;
-
-⚔ Learn about the full-stack lifecycle.
-A productive full-stack web framework should not force you to think about framework details.
\nNullstack takes control of its subclasses and generates a proxy for each instance.
\nWhen you call anything on your class you are actually telling Nullstack what to do with the environment behind the scenes.
\nThis allows you to use vanilla javascript operations like assigning to a variable and see the reflection in the dom.
\nYou can mutate instance variables to update your application state.
\nFunctions are automatically bound to the instance proxy and can be passed as a reference to events.
\nEvents are declared like normal HTML attributes.
\nimport Nullstack from 'nullstack';\n\nclass Counter extends Nullstack {\n\n count = 0;\n\n increment() {\n this.count++;\n }\n \n render() {\n return (\n <button onclick={this.increment}> \n {this.count}\n </button>\n )\n }\n\n}\n\nexport default Counter;\n\n\n\n💡 Updates are made in batches, usually while awaiting async calls, so making multiple assignments have no performance costs!
\n
You can shortcut events that are simple assignments by passing an object to the event.
\nEach key of the object will be assigned to the instance.
\nimport Nullstack from 'nullstack';\n\nclass Counter extends Nullstack {\n\n count = 0;\n \n render() {\n return (\n <button onclick={{count: this.count + 1}}> \n {this.count}\n </button>\n )\n }\n\n}\n\nexport default Counter;\n\nBy default, events refer to this when you pass an object.
\nYou can use the source attribute to define which object will receive the assignments.
\nimport Nullstack from 'nullstack';\n\nclass Paginator extends Nullstack {\n \n render({params}) {\n return (\n <button source={params} onclick={{page: 1}}> \n First Page\n </button>\n )\n }\n\n}\n\nexport default Paginator;\n\n\n\n✨ Learn more about context params.
\n
\n\n💡 If you do not declare a source to the event, Nullstack will inject a source={this} at transpile time in order to completely skip the runtime lookup process!
\n
Attributes of the event target will be merged to the instance context and can be destructured in the function signature.
\nimport Nullstack from 'nullstack';\n\nclass Counter extends Nullstack {\n\n count = 0;\n\n increment({delta}) {\n this.count += delta;\n }\n \n render() {\n return (\n <button onclick={this.increment} delta={2}> \n {this.count}\n </button>\n )\n }\n\n}\n\nexport default Counter;\n\n\n\n💡 Any attribute with primitive value will be added to the DOM.
\n
\n\n✨ Consider using data attributes to make your html valid.
\n
The browser default behavior is prevented by default.
\nYou can opt-out of this by declaring a default attribute to the event element.
\nA reference to the original event is always merged with the function context.
\nimport Nullstack from 'nullstack';\n\nclass Form extends Nullstack {\n\n submit({event}) {\n event.preventDefault();\n }\n \n render() {\n return (\n <form onsubmit={this.submit} default>\n <button> Submit </button>\n </form>\n )\n }\n\n}\n\nexport default Form;\n\n⚔ Learn about the full-stack lifecycle.
\n","description":"A productive full-stack web framework should not force you to think about framework details"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Stateful Components - Nullstack","description":"A productive full-stack web framework should not force you to think about framework details"}} \ No newline at end of file diff --git a/docs/static-site-generation/index.html b/docs/static-site-generation/index.html deleted file mode 100644 index 93557830..00000000 --- a/docs/static-site-generation/index.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - -Use Nullstack to generate static websites for lightning-fast static applications using the full power of the Nullstack client without the need for a node.js back-end.
-Static sites are useful for read-only applications like blogs and documentation.
---💡 This documentation is actually a static site generated with Nullstack.
-
All the benefits of server-side rendering apply to static generated sites.
-You can generate a static website from your Nullstack application with the following NPX command:
-npx create-nullstatic-app
-
---🔥 You must be in a Nullstack project folder to run this command.
-
By default, it will create your Nullstatic application in the static folder.
-You can change the folder by passing it as an argument to the command:
-npx create-nullstatic-app docs
-
-The Nullstatic generator will run your application in production mode and crawl every link to an internal route it finds in your DOM.
---💡 Make sure to have the server production port free when you run this command.
-
The manifest.json and the contents of the public folder will be copied into the target folder.
-Besides generating raw HTML it will also generate a JSON file for each route with a copy of the state.
-On the first visit to your static application, HTML will be served and hydrated.
-On the subsequent requests, Nullstack will fetch the generated JSON and update the application state without ever reloading the page.
-This, in fact, gives you not only a static generated site, but a static generated API that feeds a Single Page Application with zero costs.
-You can add a script to your package.json to generate your static website in a custom folder:
-{
- "name": "nullstack.github.io",
- "version": "0.0.1",
- "description": "",
- "author": "",
- "license": "ISC",
- "devDependencies": {
- "nullstack": "~0.9.0"
- },
- "scripts": {
- "start": "npx webpack --config node_modules/nullstack/webpack.config.js --mode=development --watch",
- "build": "npx webpack --config node_modules/nullstack/webpack.config.js --mode=production",
- "ssg": "npx create-nullstatic-app docs"
- }
-}
-
-
-Nullstatic only crawls your application up to the initiate resolution, further API requests triggered by events will be ignored.
-Nullstatic will crawl a /404 URL and generate both a /404.html and a /404/index.html.
-⚔ Learn more about the service worker.
-Use Nullstack to generate static websites for lightning-fast static applications using the full power of the Nullstack client without the need for a node.js back-end.
\nStatic sites are useful for read-only applications like blogs and documentation.
\n\n\n💡 This documentation is actually a static site generated with Nullstack.
\n
All the benefits of server-side rendering apply to static generated sites.
\nYou can generate a static website from your Nullstack application with the following NPX command:
\nnpx create-nullstatic-app\n\n\n\n🔥 You must be in a Nullstack project folder to run this command.
\n
By default, it will create your Nullstatic application in the static folder.
\nYou can change the folder by passing it as an argument to the command:
\nnpx create-nullstatic-app docs\n\nThe Nullstatic generator will run your application in production mode and crawl every link to an internal route it finds in your DOM.
\n\n\n💡 Make sure to have the server production port free when you run this command.
\n
The manifest.json and the contents of the public folder will be copied into the target folder.
\nBesides generating raw HTML it will also generate a JSON file for each route with a copy of the state.
\nOn the first visit to your static application, HTML will be served and hydrated.
\nOn the subsequent requests, Nullstack will fetch the generated JSON and update the application state without ever reloading the page.
\nThis, in fact, gives you not only a static generated site, but a static generated API that feeds a Single Page Application with zero costs.
\nYou can add a script to your package.json to generate your static website in a custom folder:
\n{\n \"name\": \"nullstack.github.io\",\n \"version\": \"0.0.1\",\n \"description\": \"\",\n \"author\": \"\",\n \"license\": \"ISC\",\n \"devDependencies\": {\n \"nullstack\": \"~0.9.0\"\n },\n \"scripts\": {\n \"start\": \"npx webpack --config node_modules/nullstack/webpack.config.js --mode=development --watch\",\n \"build\": \"npx webpack --config node_modules/nullstack/webpack.config.js --mode=production\",\n \"ssg\": \"npx create-nullstatic-app docs\"\n }\n}\n\n\nNullstatic only crawls your application up to the initiate resolution, further API requests triggered by events will be ignored.
\nNullstatic will crawl a /404 URL and generate both a /404.html and a /404/index.html.
\n⚔ Learn more about the service worker.
\n","description":"Use Nullstack to generate static websites for lightning-fast static applications using the full power of Nullstack without the need for a node.js back-end"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Static Site Generation - Nullstack","description":"Use Nullstack to generate static websites for lightning-fast static applications using the full power of Nullstack without the need for a node.js back-end"}} \ No newline at end of file diff --git a/docs/styles/index.html b/docs/styles/index.html deleted file mode 100644 index f1960e76..00000000 --- a/docs/styles/index.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - -Using styles with Nullstack is as simple as importing a style file.
-Nullstack comes with a SASS loader by default, but you can still use vanilla CSS.
---✨ It's a good practice to import a file with the same name as the component.
-
import Nullstack from 'nullstack';
-import './Header.scss';
-
-class Header extends Nullstack {
- // ...
-}
-
-export default Header;
-
-In production mode Nullstack uses PurceCSS, which cleans your client.css file, but has some gotchas.
---✨ Learn more about safelisting your css
-
⚔ Learn about the NJS file extension.
-Using styles with Nullstack is as simple as importing a style file.
\nNullstack comes with a SASS loader by default, but you can still use vanilla CSS.
\n\n\n✨ It's a good practice to import a file with the same name as the component.
\n
import Nullstack from 'nullstack';\nimport './Header.scss';\n\nclass Header extends Nullstack {\n // ...\n}\n\nexport default Header;\n\nIn production mode Nullstack uses PurceCSS, which cleans your client.css file, but has some gotchas.
\n\n\n✨ Learn more about safelisting your css
\n
⚔ Learn about the NJS file extension.
\n","description":"Using styles with Nullstack is as simple as importing a style file"},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Styles - Nullstack","description":"Using styles with Nullstack is as simple as importing a style file"}} \ No newline at end of file diff --git a/docs/two-way-bindings/index.html b/docs/two-way-bindings/index.html deleted file mode 100644 index c9811c91..00000000 --- a/docs/two-way-bindings/index.html +++ /dev/null @@ -1,289 +0,0 @@ - - - - - - -Big chunks of code in a progressive web application is dedicated to reacting to user input.
-The process of controlling user input can be broken into 3 tedious steps:
-The last step might include typecasting and other value treatments.
-This process in which you manually do all these steps is called one-way binding, it is the default in many frameworks, and is possible in Nullstack.
-import Nullstack from 'nullstack';
-
-class Form extends Nullstack {
-
- number = 1;
- string = '';
-
- updateString({event}) {
- this.string = event.target.value;
- }
-
- updateNumber({event}) {
- this.number = parseInt(event.target.value);
- }
-
- render() {
- return (
- <form>
- <input
- type="text"
- name="string"
- value={this.string}
- oninput={this.updateString}
- />
- <input
- type="number"
- name="number"
- value={this.number}
- oninput={this.updateNumber}
- />
- </form>
- )
- }
-
-}
-
-export default Form;
-
-Bind reduces drastically the amount of glue code you have to type in your application.
-You can shortcut setting a value, name, and event with the bind attribute.
---💡 Nullstack will simply replace bind with the value, name, and event under the hood.
-
Bind will generate an event that automatically typecasts to the previous primitive type the value was.
-You can pass any variable to the bind as long as its parent object is mentioned.
-import Nullstack from 'nullstack';
-
-class Form extends Nullstack {
-
- number = 1;
- string = '';
-
- render() {
- return (
- <form>
- <input type="text" bind={this.string} />
- <input type="number" bind={this.number} />
- </form>
- )
- }
-
-}
-
-export default Form;
-
-The following events are set for each type of input:
-You can still declare an attribute with the same bound event.
-Events will not override the bound event, instead, it will be executed after bind mutates the variable.
-The new value will be merged into the function context.
-import Nullstack from 'nullstack';
-
-class Form extends Nullstack {
-
- name = '';
-
- compare({value}) {
- this.name === value;
- }
-
- render() {
- return (
- <input bind={this.name} oninput={this.compare} />
- )
- }
-
-}
-
-export default Form;
-
-Bind can take a source attribute as well.
---💡 If you do not declare a source to the bind, Nullstack will inject a source={this} at transpile time in order to completely skip the runtime lookup process!
-
If you declare a source, bind must be a string with the name of the key that will be mutated.
-The source will be merged into the context of events.
-import Nullstack from 'nullstack';
-
-class Paginator extends Nullstack {
-
- validate({source, params}) {
- if(!source.page) {
- params.page = '1';
- }
- }
-
- render({params}) {
- return (
- <input
- source={params}
- bind="page"
- oninput={this.validate}
- />
- )
- }
-
-}
-
-export default Paginator;
-
---💡 Binding by reference is possible because all binds are converted to the format above at transpile time.
-
Any object that responds to a key call with "[]" can be bound.
-The name attribute can be overwritten.
-import Nullstack from 'nullstack';
-
-class Form extends Nullstack {
-
- number = 1;
- boolean = true;
- character = 'a';
- text = 'aaaa';
-
- object = {count: 1};
- array = ['a', 'b', 'c'];
-
- render({params}) {
- return (
- <div>
- <input bind={this.number} />
- <textarea bind={this.text} />
- <select bind={this.character}>
- {this.array.map((character) => <option>{character}</option>)}
- </select>
- <select bind={this.boolean} name="boolean-select">
- <option value={true}>true</option>
- <option value={false}>false</option>
- </select>
- <input bind={this.boolean} type="checkbox" />
- <input bind={this.object.count} />
- {this.array.map((value, index) => (
- <input bind={this.array[index]} />
- ))}
- <input bind={params.page} />
- </div>
- )
- }
-
-}
-
-export default Form;
-
-You can use object events alongside bind normally.
-The event will run after the variable is mutated.
-The event will share the bind source.
-import Nullstack from 'nullstack';
-
-class Paginator extends Nullstack {
-
- render({params}) {
- return (
- <input bind={params.filter} oninput={{page: 1}} />
- )
- }
-
-}
-
-export default Paginator;
-
-You can create your own bindable component by receiving the attributes that bind generates.
-You must respond by calling onchange with a value key.
-You can also merge any other keys you wish to send to the component user.
-class CurrencyInput extends Nullstack {
-
- parse({event, onchange}) {
- const normalized = event.target.value.replace(',', '').padStart(3, '0');
- const whole = (parseInt(normalized.slice(0,-2)) || 0).toString();
- const decimal = normalized.slice(normalized.length - 2);
- const value = parseFloat(whole+'.'+decimal);
- const bringsHappyness = value >= 1000000;
- onchange({value, bringsHappyness});
- }
-
- render({value, name}) {
- const formatted = value.toFixed(2).replace('.', ',');
- return <input name={name} value={formatted} oninput={this.parse} />
- }
-
-}
-
-import Nullstack from 'nullstack';
-import CurrencyInput from './CurrencyInput';
-
-class Form extends Nullstack {
-
- balance = 0;
-
- render() {
- return (
- <CurrencyInput bind={this.balance} />
- )
- }
-
-}
-
-export default Form;
-
---🎉 Congratulations. You are done with the core concepts!
-
⚔ Learn about the application startup.
-Big chunks of code in a progressive web application is dedicated to reacting to user input.
\nThe process of controlling user input can be broken into 3 tedious steps:
\nThe last step might include typecasting and other value treatments.
\nThis process in which you manually do all these steps is called one-way binding, it is the default in many frameworks, and is possible in Nullstack.
\nimport Nullstack from 'nullstack';\n\nclass Form extends Nullstack {\n\n number = 1;\n string = '';\n\n updateString({event}) {\n this.string = event.target.value;\n }\n\n updateNumber({event}) {\n this.number = parseInt(event.target.value);\n }\n \n render() {\n return (\n <form>\n <input\n type=\"text\"\n name=\"string\"\n value={this.string}\n oninput={this.updateString}\n />\n <input\n type=\"number\"\n name=\"number\"\n value={this.number}\n oninput={this.updateNumber}\n />\n </form>\n )\n }\n\n}\n\nexport default Form;\n\nBind reduces drastically the amount of glue code you have to type in your application.
\nYou can shortcut setting a value, name, and event with the bind attribute.
\n\n\n💡 Nullstack will simply replace bind with the value, name, and event under the hood.
\n
Bind will generate an event that automatically typecasts to the previous primitive type the value was.
\nYou can pass any variable to the bind as long as its parent object is mentioned.
\nimport Nullstack from 'nullstack';\n\nclass Form extends Nullstack {\n\n number = 1;\n string = '';\n \n render() {\n return (\n <form>\n <input type=\"text\" bind={this.string} />\n <input type=\"number\" bind={this.number} />\n </form>\n )\n }\n\n}\n\nexport default Form;\n\nThe following events are set for each type of input:
\nYou can still declare an attribute with the same bound event.
\nEvents will not override the bound event, instead, it will be executed after bind mutates the variable.
\nThe new value will be merged into the function context.
\nimport Nullstack from 'nullstack';\n\nclass Form extends Nullstack {\n\n name = '';\n\n compare({value}) {\n this.name === value;\n }\n \n render() {\n return (\n <input bind={this.name} oninput={this.compare} />\n )\n }\n\n}\n\nexport default Form;\n\nBind can take a source attribute as well.
\n\n\n💡 If you do not declare a source to the bind, Nullstack will inject a source={this} at transpile time in order to completely skip the runtime lookup process!
\n
If you declare a source, bind must be a string with the name of the key that will be mutated.
\nThe source will be merged into the context of events.
\nimport Nullstack from 'nullstack';\n\nclass Paginator extends Nullstack {\n\n validate({source, params}) {\n if(!source.page) {\n params.page = '1';\n }\n }\n\n render({params}) {\n return (\n <input \n source={params}\n bind=\"page\"\n oninput={this.validate}\n />\n )\n }\n\n}\n\nexport default Paginator;\n\n\n\n💡 Binding by reference is possible because all binds are converted to the format above at transpile time.
\n
Any object that responds to a key call with "[]" can be bound.
\nThe name attribute can be overwritten.
\nimport Nullstack from 'nullstack';\n\nclass Form extends Nullstack {\n\n number = 1;\n boolean = true;\n character = 'a';\n text = 'aaaa';\n \n object = {count: 1};\n array = ['a', 'b', 'c'];\n\n render({params}) {\n return (\n <div>\n <input bind={this.number} />\n <textarea bind={this.text} />\n <select bind={this.character}>\n {this.array.map((character) => <option>{character}</option>)}\n </select>\n <select bind={this.boolean} name=\"boolean-select\">\n <option value={true}>true</option>\n <option value={false}>false</option>\n </select>\n <input bind={this.boolean} type=\"checkbox\" />\n <input bind={this.object.count} />\n {this.array.map((value, index) => (\n <input bind={this.array[index]} />\n ))}\n <input bind={params.page} />\n </div>\n )\n }\n\n}\n\nexport default Form;\n\nYou can use object events alongside bind normally.
\nThe event will run after the variable is mutated.
\nThe event will share the bind source.
\nimport Nullstack from 'nullstack';\n\nclass Paginator extends Nullstack {\n\n render({params}) {\n return (\n <input bind={params.filter} oninput={{page: 1}} />\n )\n }\n\n}\n\nexport default Paginator;\n\nYou can create your own bindable component by receiving the attributes that bind generates.
\nYou must respond by calling onchange with a value key.
\nYou can also merge any other keys you wish to send to the component user.
\nclass CurrencyInput extends Nullstack {\n\n parse({event, onchange}) {\n const normalized = event.target.value.replace(',', '').padStart(3, '0');\n const whole = (parseInt(normalized.slice(0,-2)) || 0).toString();\n const decimal = normalized.slice(normalized.length - 2);\n const value = parseFloat(whole+'.'+decimal);\n const bringsHappyness = value >= 1000000;\n onchange({value, bringsHappyness});\n }\n\n render({value, name}) {\n const formatted = value.toFixed(2).replace('.', ',');\n return <input name={name} value={formatted} oninput={this.parse} />\n }\n\n}\n\nimport Nullstack from 'nullstack';\nimport CurrencyInput from './CurrencyInput';\n\nclass Form extends Nullstack {\n\n balance = 0;\n\n render() {\n return (\n <CurrencyInput bind={this.balance} />\n )\n }\n\n}\n\nexport default Form;\n\n\n\n🎉 Congratulations. You are done with the core concepts!
\n
⚔ Learn about the application startup.
\n","description":"Bind reduces drastically the amount of glue code you have to type in your application."},"_.0.0.8":{},"_.0.0.9":{}}, "page": {"image":"/image-1200x630.png","status":200,"locale":"en","title":"Two-Way Binding - Nullstack","description":"Bind reduces drastically the amount of glue code you have to type in your application."}} \ No newline at end of file diff --git a/docs/waifu.png b/docs/waifu.png deleted file mode 100644 index 2154b9b7..00000000 Binary files a/docs/waifu.png and /dev/null differ diff --git a/docs/waifu/index.html b/docs/waifu/index.html deleted file mode 100644 index 3448c846..00000000 --- a/docs/waifu/index.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - -
Nullstack's official waifu
A sweet and shy perfect waifu, is always hyperfocused in her studies but somehow distracted at the same time.
She is always cheerful... until she has to face glue code or sees a post telling her which tech she should use.
I'm in the client
} + {environment.server &&I'm in the server
} + {environment.development &&I'm in development mode
} + {environment.production &&I'm in production mode
} + {environment.static &&I'm in a static site
} +My key is {environment.key}
+Count: {this.value}
+ } + +} + +export default Counter; +``` + +You can access any methods and instance variables from **counter** instance on **AnyOtherComponent**: + +```jsx +import Nullstack from 'nullstack'; + +class AnyOtherComponent extends Nullstack { + + render({ instances }) { + return ( + + ) + } + +} + +export default AnyOtherComponent; +``` + +The use of `instances` unlocks unlimited custom behaviors like: + +- A notification icon at the navbar that can be updated from other components at certain actions +- A *toast* component that can be invoked from anywhere in your application +- A *store* system with custom dispatch methods similar to Redux +- Something we haven't even imagined, dream on and post your ideas on GitHub! \ No newline at end of file diff --git a/i18n/en-US/articles/context-page.md b/i18n/en-US/articles/context-page.md new file mode 100644 index 00000000..351786e9 --- /dev/null +++ b/i18n/en-US/articles/context-page.md @@ -0,0 +1,136 @@ +--- +title: Context Page +description: The page object is a proxy in the Nullstack Context available in both client and server and gives you information about the document head metatags +--- + +- Type: `object` +- Origin: [Nullstack Context](/context#----nullstack-context) +- Availability: server/client +- **readwrite** in server/client context + +It gives you information about the document `head` metatags. + +`page` keys will be used to generate metatags during [server-side rendering](/server-side-rendering) and must be assigned before [`initiate`](/full-stack-lifecycle) while this resolved. + +The following keys are available in the object: + +- **title**: `string` +- **image**: `string` (absolute or relative URL) +- **description**: `string` +- **canonical**: `string` (absolute or relative URL) +- **locale**: `string` +- **robots**: `string` +- **schema**: `object` +- **changes**: `string` +- **priority**: `number` +- **status**: `number` + +When the `title` key is assigned on the client-side, the document title will be updated. + +Nullstack uses the `changes` and `priority` keys to generate the **sitemap.xml**. + +The sitemap is generated automatically only when using [static site generation](/static-site-generation) and must be manually generated in [server-side rendered](/server-side-rendering) applications. + +The `changes` key represents the `changefreq` key in the **sitemap.xml** and if assigned must be one of the following values: + +- **always** +- **hourly** +- **daily** +- **weekly** +- **monthly** +- **yearly** +- **never** + +The `priority` key is a number between `0.0` and `1.0` that represents the `priority` key in the **sitemap.xml**. + +Nullstack does not set a default priority, however, sitemaps assume a `0.5` priority when not explicitly set. + +Besides `title` and `locale` all other keys have sensible defaults generated based on the application scope. + +```jsx +import Nullstack from 'nullstack'; + +class Page extends Nullstack { + + prepare({project, page}) { + page.title = `${project.name} - Page Title`; + page.image = '/image.jpg'; + page.description = 'Page meta description'; + page.canonical = 'http://absolute.url/canonical-link'; + page.locale = 'pt-BR'; + page.robots = 'index, follow'; + page.schema = {}; + page.changes = 'weekly'; + page.priority = 1; + } + + render({page}) { + return ( +{page.description}
+