marko 5.17.9 → 5.18.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,34 @@
1
+ # Marko + Cloudflare Workers
2
+
3
+ See the [the cloudflare sample](https://github.com/marko-js/examples/tree/master/examples/vite-cloudflare)
4
+ project for a working example.
5
+
6
+ ## Usage
7
+
8
+ When using Marko with [Cloudflare Workers](https://workers.cloudflare.com/) you need to make sure that Marko is loaded with a `worker` [export condition](https://nodejs.org/api/packages.html#conditional-exports). Most bundlers support the ability to define export conditions.
9
+
10
+ After that point the `template.stream` will now return a worker compatible [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream).
11
+
12
+ You can then simply respond with the returned stream.
13
+
14
+ ```js
15
+ import template from "./index.marko";
16
+
17
+ addEventListener("fetch", event => {
18
+ event.respondWith(handleRequest(event.request));
19
+ });
20
+
21
+ async function handleRequest(request) {
22
+ return new Response(template.stream(), {
23
+ headers: {
24
+ status: 200,
25
+ headers: { "content-type": "text/html;charset=UTF-8" }
26
+ }
27
+ });
28
+ }
29
+ ```
30
+
31
+ ### BYOB (Bring your own bundler)
32
+
33
+ For the large portion of Marko's API a bundler is required. The example code above assumes that Marko templates can be loaded in your environment.
34
+ Marko supports a number of bundlers, [take a look through our supported bundlers](#bundler-integrations) and pick what works best for you.
@@ -184,3 +184,18 @@ The [`<macro>`](./core-tags.md#macro) tag allows you to create custom tags in th
184
184
  <welcome-message name="Patrick"/>
185
185
  <welcome-message name="Austin"/>
186
186
  ```
187
+
188
+ # From Variables
189
+
190
+ If no other tag would be discovered Marko will check for an in scope variable that matches the tag name.
191
+
192
+ ```marko
193
+ import SomeTag from "./somewhere.marko"
194
+
195
+ $ const { renderBody } = input;
196
+ $ const MyTag = input.href ? "a" : "button";
197
+
198
+ <SomeTag/>
199
+ <MyTag/>
200
+ <renderBody/>
201
+ ```
package/docs/events.md CHANGED
@@ -124,7 +124,7 @@ The above code listens to native `change` events from the `<input>` element, and
124
124
 
125
125
  ```marko
126
126
  <form>
127
- <email-input name="email" on-email-change(...)/>
127
+ <email-input name="email" on-email-change("...")/>
128
128
  </form>
129
129
  ```
130
130
 
package/docs/express.md CHANGED
@@ -1,38 +1,40 @@
1
- # Express + Marko
1
+ # Marko + Express
2
2
 
3
- See the [marko-express](https://github.com/marko-js/examples/tree/master/examples/lasso-express) sample
3
+ ## Quick Start
4
+
5
+ ```terminal
6
+ npm init marko -- --template vite-express
7
+ ```
8
+
9
+ See the [the express sample](https://github.com/marko-js/examples/tree/master/examples/vite-express)
4
10
  project for a working example.
5
11
 
6
- ## Installation
12
+ ## From Scratch
7
13
 
8
- Need to install mark as well as the express-related marko package:
14
+ First install Marko and the express related dependencies:
9
15
 
10
- ```
11
- npm install express --save
12
- npm install marko --save
13
- npm install @marko/express --save
16
+ ```terminal
17
+ npm install marko @marko/express express --save
14
18
  ```
15
19
 
16
- ## Skip the view engine
20
+ ### Skip the view engine
17
21
 
18
- The built in view engine for express may be asynchronous, but it doesn't support streaming (check out [Rediscovering Progressive HTML Rendering](http://www.ebaytechblog.com/2014/12/08/async-fragments-rediscovering-progressive-html-rendering-with-marko/) to see why this is so important). So instead we'll [bypass the view engine](https://strongloop.com/strongblog/bypassing-express-view-rendering-for-speed-and-modularity/).
22
+ The built in view engine for express may be asynchronous, but it doesn't support streaming (check out [Rediscovering Progressive HTML Rendering](http://www.ebaytechblog.com/2014/12/08/async-fragments-rediscovering-progressive-html-rendering-with-marko/) to see why this is so important). So instead we'll [bypass the view engine](https://strongloop.com/strongblog/bypassing-express-view-rendering-for-speed-and-modularity/) and use [`@marko/express`](https://github.com/marko-js/express/).
19
23
 
20
- ## Usage
24
+ ### Usage
21
25
 
22
- Marko provides a package (`@marko/express`) to add a `res.marko` method to the express response object. This function works much like `res.render`, but doesn't impose the restrictions of the express view engine and allows you to take full advantage of Marko's streaming and modular approach to templates.
26
+ The [`@marko/express`](https://github.com/marko-js/express/) adds a `res.marko` method to the express response object. This function works much like `res.render`, but doesn't impose the restrictions of the express view engine and allows you to take full advantage of Marko's streaming and modular approach to templates.
23
27
 
24
28
  By using `res.marko` you'll automatically have access to `req`, `res`, `app`, `app.locals`, and `res.locals` from within your Marko template and custom tags. These values are added to `out.global`.
25
29
 
26
30
  ```javascript
27
- require("@marko/compiler/register"); // Allow Node.js to require and load `.marko` files
31
+ import express from "express";
32
+ import markoPlugin from "@marko/express";
33
+ import template from "./template.marko";
28
34
 
29
- var express = require("express");
30
- var markoExpress = require("@marko/express");
31
- var template = require("./template");
35
+ const app = express();
32
36
 
33
- var app = express();
34
-
35
- app.use(markoExpress()); //enable res.marko(template, data)
37
+ app.use(markoPlugin()); //enable res.marko(template, data)
36
38
 
37
39
  app.get("/", function (req, res) {
38
40
  res.marko(template, {
@@ -44,3 +46,8 @@ app.get("/", function (req, res) {
44
46
 
45
47
  app.listen(8080);
46
48
  ```
49
+
50
+ ### BYOB (Bring your own bundler)
51
+
52
+ For the large portion of Marko's API a bundler is required. The example code above assumes that Marko templates can be loaded in your environment.
53
+ Marko supports a number of bundlers, [take a look through our supported bundlers](#bundler-integrations) and pick what works best for you.
package/docs/fastify.md CHANGED
@@ -1,37 +1,47 @@
1
- # Fastify + Marko
1
+ # Marko + Fastify
2
2
 
3
- See the [lasso-fastify](https://github.com/marko-js/examples/tree/master/examples/lasso-fastify) sample
4
- project for a fully-working example.
3
+ ## Quick Start
5
4
 
6
- ## Installation
5
+ ```terminal
6
+ npm init marko -- --template vite-fastify
7
+ ```
8
+
9
+ See the [the fastify sample](https://github.com/marko-js/examples/tree/master/examples/vite-fastify)
10
+ project for a working example.
11
+
12
+ ## From Scratch
7
13
 
8
- ```bash
9
- npm install fastify --save
10
- npm install point-of-view --save
11
- npm install marko --save
14
+ First install Marko and the fastify related dependencies:
15
+
16
+ ```terminal
17
+ npm install marko @marko/fastify fastify --save
12
18
  ```
13
19
 
14
- ## Usage
20
+ ### Usage
15
21
 
16
- ```js
17
- const fastify = require("fastify")();
22
+ The [`@marko/fastify`](https://github.com/marko-js/fastify/) adds a `reply.marko` decorator to the `reply` object. This function allows us to pass in a Marko template and supports Marko's streaming and modular approach to templates.
18
23
 
19
- fastify.register(require("point-of-view"), {
20
- engine: {
21
- marko: require("marko")
22
- }
23
- });
24
+ By using `reply.marko` you'll automatically have access to `app.locals`, and `reply.locals` from within your Marko template and custom tags. These values are added to `out.global`.
24
25
 
25
- fastify.get("/", (req, reply) => {
26
- reply.view("/index.marko", {
27
- name: "Frank",
28
- count: 30,
29
- colors: ["red", "green", "blue"]
30
- });
31
- });
26
+ ```javascript
27
+ import fastify from "fastify";
28
+ import markoPlugin from "@marko/fastify";
29
+ import Template from "./template.marko";
32
30
 
33
- fastify.listen(8080, err => {
34
- if (err) throw err;
35
- console.log(`Server listening on ${fastify.server.address().port}`);
31
+ const app = fastify();
32
+
33
+ app.register(markoPlugin);
34
+
35
+ app.get("/", (request, reply) => {
36
+ // Streams Marko template into the response.
37
+ // Forwards errors into fa error handler.
38
+ reply.marko(Template, { hello: "world" });
36
39
  });
40
+
41
+ await fastify.listen(3000);
37
42
  ```
43
+
44
+ ### BYOB (Bring your own bundler)
45
+
46
+ For the large portion of Marko's API a bundler is required. The example code above assumes that Marko templates can be loaded in your environment.
47
+ Marko supports a number of bundlers, [take a look through our supported bundlers](#bundler-integrations) and pick what works best for you.
package/docs/http.md CHANGED
@@ -1,27 +1,19 @@
1
1
  # Marko + HTTP Server
2
2
 
3
- See the [marko-http](https://github.com/marko-js/examples/tree/master/examples/http) sample
3
+ See the [the http sample](https://github.com/marko-js/examples/tree/master/examples/vite-http)
4
4
  project for a working example.
5
5
 
6
- ## Installation
7
-
8
- ```bash
9
- npm install marko --save
10
- ```
11
-
12
6
  ## Usage
13
7
 
14
8
  ```js
15
- require("@marko/compiler/register");
16
-
17
- const http = require("http");
18
- const server = http.createServer();
9
+ import http from "http";
10
+ import template from "./index.marko";
19
11
 
20
12
  const port = 8080;
21
- const indexTemplate = require("./index.marko");
13
+ const server = http.createServer();
22
14
 
23
15
  server.on("request", (req, res) => {
24
- indexTemplate.render(
16
+ template.render(
25
17
  {
26
18
  name: "Frank",
27
19
  count: 30,
@@ -35,3 +27,8 @@ server.listen(port, () => {
35
27
  console.log(`Successfully started server on port ${port}`);
36
28
  });
37
29
  ```
30
+
31
+ ### BYOB (Bring your own bundler)
32
+
33
+ For the large portion of Marko's API a bundler is required. The example code above assumes that Marko templates can be loaded in your environment.
34
+ Marko supports a number of bundlers, [take a look through our supported bundlers](#bundler-integrations) and pick what works best for you.
package/docs/koa.md CHANGED
@@ -1,23 +1,22 @@
1
- # Koa + Marko
1
+ # Marko + Koa
2
2
 
3
- See [the `marko-koa` sample project](https://github.com/marko-js/examples/tree/master/examples/lasso-koa) for a fully-working example.
3
+ See the [the koa sample](https://github.com/marko-js/examples/tree/master/examples/vite-koa)
4
+ project for a working example.
4
5
 
5
6
  ## Installation
6
7
 
7
- ```sh
8
+ ```terminal
8
9
  npm install koa marko --save
9
10
  ```
10
11
 
11
12
  ## Usage
12
13
 
13
14
  ```javascript
14
- require("@marko/compiler/register");
15
+ import Koa from "koa";
16
+ import template from "./index.marko";
15
17
 
16
- const Koa = require("koa");
17
18
  const app = new Koa();
18
19
 
19
- const template = require("./index.marko");
20
-
21
20
  app.use((ctx, next) => {
22
21
  ctx.type = "html";
23
22
  ctx.body = template.stream({
@@ -30,33 +29,7 @@ app.use((ctx, next) => {
30
29
  app.listen(8080);
31
30
  ```
32
31
 
33
- You may also easily add `gzip` streaming support without additional dependencies:
34
-
35
- ```javascript
36
- require("@marko/compiler/register");
37
- const zlib = require("zlib");
38
-
39
- const Koa = require("koa");
40
- const app = new Koa();
41
-
42
- const template = require("./index.marko");
43
-
44
- app.use((ctx, next) => {
45
- ctx.type = "html";
46
- ctx.body = template.stream({
47
- name: "Frank",
48
- count: 30,
49
- colors: ["red", "green", "blue"]
50
- });
51
-
52
- ctx.vary("Accept-Encoding");
53
- if (ctx.acceptsEncodings("gzip")) {
54
- ctx.set("Content-Encoding", "gzip");
55
- ctx.body = ctx.body.pipe(
56
- zlib.createGzip({ flush: zlib.constants.Z_PARTIAL_FLUSH })
57
- );
58
- }
59
- });
32
+ ### BYOB (Bring your own bundler)
60
33
 
61
- app.listen(8080);
62
- ```
34
+ For the large portion of Marko's API a bundler is required. The example code above assumes that Marko templates can be loaded in your environment.
35
+ Marko supports a number of bundlers, [take a look through our supported bundlers](#bundler-integrations) and pick what works best for you.
@@ -737,28 +737,23 @@ the server or in the browser. For example, given the following template:
737
737
 
738
738
  #### Compiled for the server:
739
739
 
740
- ```marko
740
+ ```js
741
741
  var marko_template = require("marko/html").t(__filename),
742
- marko_helpers = require("marko/runtime/html/helpers"),
743
- marko_escapeXml = marko_helpers.x;
742
+ marko_helpers = require("marko/runtime/html/helpers"),
743
+ marko_escapeXml = marko_helpers.x;
744
744
 
745
745
  function render(input, out) {
746
- out.w("<div>Hello " +
747
- marko_escapeXml(input.name) +
748
- "!</div>");
746
+ out.w("<div>Hello " + marko_escapeXml(input.name) + "!</div>");
749
747
  }
750
748
  ```
751
749
 
752
750
  #### Compiled for the browser:
753
751
 
754
- ```marko
752
+ ```js
755
753
  var marko_template = require("marko/vdom").t(__filename);
756
754
 
757
755
  function render(input, out) {
758
- out.e("DIV", null, 3)
759
- .t("Hello ")
760
- .t(input.name)
761
- .t("!");
756
+ out.e("DIV", null, 3).t("Hello ").t(input.name).t("!");
762
757
  }
763
758
  ```
764
759
 
package/docs/rollup.md CHANGED
@@ -1,22 +1,16 @@
1
1
  # Marko + Rollup
2
2
 
3
- The [@marko/rollup](https://github.com/marko-js/rollup) transform can be used in conjunction with [rollup](https://github.com/rollup/rollup) to automatically compile Marko templates that are required by other modules.
3
+ # Installation
4
4
 
5
- <!-- The [rollup](https://github.com/marko-js/examples/tree/master/examples/rollup) sample app demonstrates how to use Marko with Rollup. Run `npx @marko/create --template rollup` to use this sample as a starting point for a new app. -->
6
-
7
- ## Manual Installation
8
-
9
- ```bash
10
- npm install rollup @rollup/plugin-commonjs @rollup/plugin-node-resolve @marko/rollup --save-dev
5
+ ```console
6
+ npm install @marko/rollup rollup @rollup/plugin-node-resolve @rollup/plugin-commonjs -D
11
7
  ```
12
8
 
13
- ## Configuration
9
+ # Basic example config
14
10
 
15
- The following is the minimal recommend configuration to use Rollup with Marko:
11
+ **Note: The Marko runtime is authored in commonjs, this means the `@rollup/plugin-commonjs` is required!**
16
12
 
17
- _rollup.config.js_
18
-
19
- ```js
13
+ ```javascript
20
14
  import nodeResolve from "@rollup/plugin-node-resolve";
21
15
  import commonjs from "@rollup/plugin-commonjs";
22
16
  import marko from "@marko/rollup";
@@ -24,12 +18,12 @@ import marko from "@marko/rollup";
24
18
  export default {
25
19
  ...,
26
20
  plugins: [
27
- marko(),
21
+ marko.browser(),
28
22
  nodeResolve({
29
23
  browser: true,
30
24
  extensions: [".js", ".marko"]
31
25
  }),
32
- // NOTE: Marko 4 compiles to commonjs, this plugin is also required.
26
+ // NOTE: The Marko runtime uses commonjs so this plugin is also required.
33
27
  commonjs({
34
28
  extensions: [".js", ".marko"]
35
29
  }),
@@ -41,12 +35,166 @@ export default {
41
35
  };
42
36
  ```
43
37
 
44
- ## Usage
38
+ Likewise, if bundling the components for the server use `marko.server()` as the plugin.
39
+
40
+ # Linked config
41
+
42
+ If you use _both_ the `server` and `browser` plugins (in a [multi rollup config setup](https://rollupjs.org/guide/en/#configuration-files:~:text=export%20an%20array)) `@marko/rollup` will go into a _linked_ mode.
43
+ In the linked mode you will have access to the [`<rollup>` tag](#rollup-tag) on the server, and the browser config
44
+ will automatically have the [`input`](https://rollupjs.org/guide/en/#input) option set.
45
+
46
+ ```javascript
47
+ export default [{
48
+ // Config object for bundling server assets.
49
+ input: "src/your-server-entry.js",
50
+ plugins: [
51
+ marko.server()
52
+ ...
53
+ ]
54
+ }, {
55
+ // Config object for bundling browser assets.
56
+ plugins: [
57
+ marko.browser()
58
+ ...
59
+ ]
60
+ }];
61
+ ```
62
+
63
+ ## `<rollup>` tag
64
+
65
+ In a [linked setup](#linked-config) you have access to the `<rollup>` tag which will provide two [tag parameters](https://markojs.com/docs/syntax/#parameters) that allow you to write out the asset links for your server rendered app.
66
+
67
+ The first parameter `entry` is the generated `input` name that the server plugin gave to the browser compiler.
68
+ You can use it to find the corresponding entry chunk from rollups build.
69
+
70
+ The second parameter `output` is an array of `AssetInfo | ChunkInfo` objects with most of the same properties returned from rollup's [`generateBundle` hook](https://rollupjs.org/guide/en/#generatebundle). Some properties have been stripped, notably `code` and `map` since they would be too large to inline directly. A `size` property is also available for all chunks to allow you to be able to filter out empty chunks, or inline chunks of certain size.
71
+
72
+ ```marko
73
+ <head>
74
+ <rollup|entry, output|>
75
+ $ const entryChunk = output.find(chunk => chunk.name === entry);
76
+
77
+ <if(entryChunk.size /* skip scripts all together if empty js file */)>
78
+ <for|fileName| of=entryChunk.imports>
79
+ <link rel="modulepreload" href=fileName/>
80
+ </for>
81
+
82
+ <script async type="module" src=entryChunk.fileName/>
83
+ </if>
84
+ </rollup>
85
+ </head>
86
+ ```
87
+
88
+ Ultimately it is up to you to map the chunk data (sometimes referred to as a manifest) into the `<link>`'s and `<script>`'s rendered by your application.
89
+
90
+ If your rollup browser config contains multiple `output` options, or you have multiple browser configs, all of the `chunks` for each `output` are passed into the `<rollup>` tag.
91
+
92
+ For example if you have an `esm` and `iife` build:
93
+
94
+ ```javascript
95
+ {
96
+ plugins: [
97
+ marko.browser()
98
+ ...
99
+ ],
100
+ output: [
101
+ { dir: 'dist/iife', format: 'iife' },
102
+ { dir: 'dist/esm', format: 'esm' }
103
+ ]
104
+ }
105
+ ```
106
+
107
+ we could access the assets from both builds:
45
108
 
46
- ```bash
47
- # Development:
48
- rollup -c rollup.config.js
109
+ ```marko
110
+ <head>
111
+ <rollup|entry, iifeOutput, esmOutput|>
112
+ $ const iifeEntryChunk = iifeOutput.find(chunk => chunk.name === entry);
113
+ $ const esmEntryChunk = esmOutput.find(chunk => chunk.name === entry);
49
114
 
50
- # Production:
51
- NODE_ENV=production rollup -c rollup.config.js
115
+ <script async type="module" src=esmEntryChunk.fileName/>
116
+ <script nomodule src=iifeEntryChunk.fileName></script>
117
+ </rollup>
118
+ </head>
119
+ ```
120
+
121
+ and _boom_ you now have a [`module/nomodule` setup](https://philipwalton.com/articles/using-native-javascript-modules-in-production-today/).
122
+
123
+ # Top level components
124
+
125
+ Marko was designed to send as little JavaScript to the browser as possible. One of the ways we do this is by automatically determining which templates in your app should be shipped to the browser. When rendering a template on the server, it is only necessary to bundle the styles and interactive components rendered by that template.
126
+
127
+ To send the minimal amount of Marko templates to the browser you can provide a Marko template directly as the `input`.
128
+ This will also automatically invoke code to initialize the components in the browser, so there is no need to call
129
+ `template.render` yourself in the browser.
130
+
131
+ > Note: if you are using _linked_ plugins then the server plugin will automatically tell the browser compiler which Marko templates to load.
132
+
133
+ ```js
134
+ export default {
135
+ input: "./my-marko-page.marko",
136
+ plugins: [
137
+ marko.browser(),
138
+ ...
139
+ ],
140
+ ...
141
+ }
142
+ ```
143
+
144
+ ## Options
145
+
146
+ Both the `server` and `browser` plugins can receive the same options.
147
+
148
+ ### options.babelConfig
149
+
150
+ You can manually override the Babel configuration used by passing a `babelConfig` object to the `@marko/rollup` plugin. By default Babels regular [config file resolution](https://babeljs.io/docs/en/config-files) will be used.
151
+
152
+ ```javascript
153
+ marko.browser({
154
+ babelConfig: {
155
+ presets: ["@babel/preset-env"]
156
+ }
157
+ });
158
+ ```
159
+
160
+ ### options.runtimeId
161
+
162
+ In some cases you may want to embed multiple isolated copies of Marko on the page. Since Marko relies on some `window` properties to initialize this can cause issues. For example, by default Marko will read the server rendered hydration code from `window.$components`. In Marko you can change these `window` properties by rendering with `{ $global: { runtimeId: "MY_MARKO_RUNTIME_ID" } }` as input on the server side.
163
+
164
+ This plugin exposes a `runtimeId` option produces output that automatically sets `$global.runtimeId` on the server side and initializes properly in the browser.
165
+
166
+ ```js
167
+ const runtimeId = "MY_MARKO_RUNTIME_ID";
168
+ // Make sure the `runtimeId` is the same across all of your plugins!
169
+ marko.server({ runtimeId });
170
+ marko.browser({ runtimeId });
171
+ ```
172
+
173
+ ### options.serialize
174
+
175
+ This option is only available for the `browser` plugin. It allows you to transform the list of chunks serialzed in a [_linked config_](#linked-config) to include whatever you like.
176
+ For example if you _did_ want to include the `code` property from the rollup chunk, to say inline some content, the following would work:
177
+
178
+ ```js
179
+ marko.browser({
180
+ serialize(output) {
181
+ return output.map(chunk =>
182
+ chunk.type === "asset"
183
+ ? {
184
+ type: "asset",
185
+ fileName: chunk.fileName
186
+ }
187
+ : {
188
+ type: "chunk",
189
+ name: chunk.name,
190
+ isEntry: chunk.isEntry,
191
+ fileName: chunk.fileName,
192
+ code:
193
+ chunk.code.replace(/^\s+$/, "").length < 1024
194
+ ? chunk.code
195
+ : undefined // only inline small code chunks
196
+ }
197
+ );
198
+ }
199
+ });
52
200
  ```
@@ -10,7 +10,8 @@
10
10
  "Styles",
11
11
  "Events",
12
12
  "Body content",
13
- "Marko 5 upgrade"
13
+ "Marko 5 upgrade",
14
+ "Troubleshooting Streaming"
14
15
  ]
15
16
  },
16
17
  {
@@ -30,11 +31,11 @@
30
31
  },
31
32
  {
32
33
  "title": "Bundler Integrations",
33
- "docs": ["Webpack", "Rollup", "Lasso"]
34
+ "docs": ["Vite", "Webpack", "Rollup", "Lasso"]
34
35
  },
35
36
  {
36
37
  "title": "Server Integrations",
37
- "docs": ["Express", "Koa", "HTTP", "Fastify"]
38
+ "docs": ["Cloudflare Workers", "Express", "Fastify", "Koa", "HTTP"]
38
39
  },
39
40
  {
40
41
  "title": "Tooling",
package/docs/syntax.md CHANGED
@@ -358,6 +358,16 @@ _HTML Output:_
358
358
  <button>Click me!</button>
359
359
  ```
360
360
 
361
+ As a shorthand if there is a variable in scope and [no other matching tag is discovered](#how-tags-are-discovered) the wrapping `${}` is unnecessary.
362
+
363
+ For example the following are equivalent:
364
+
365
+ ```marko
366
+ $ const MyTag = href ? 'a' : 'button';
367
+ <${MyTag}/>
368
+ <MyTag/>
369
+ ```
370
+
361
371
  > **ProTip:**
362
372
  > If you find that you have a wrapper element that is conditional, but whose body should always be rendered then you can use a null dynamic tag. For example, to only render a wrapping `<a>` tag if there is a valid URL then you could do the following:
363
373
  >
@@ -0,0 +1,62 @@
1
+ # Troubleshooting HTTP Streams
2
+
3
+ [The way Marko streams HTML](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding) is old and well-supported, but default configurations and assumptions by other software can foil it. This page describes some known culprits that may buffer your Node server’s output HTTP streams.
4
+
5
+ ## Reverse proxies/load balancers
6
+
7
+ - Turn off proxy buffering, or if you can’t, set the proxy buffer sizes to be reasonably small.
8
+
9
+ - Make sure the “upstream” HTTP version is 1.1 or higher; HTTP/1.0 and lower do not support streaming.
10
+
11
+ - Some software doesn’t support HTTP/2 or higher “upstream” connections at all or very well — if your Node server uses HTTP/2, you may need to downgrade.
12
+
13
+ - Automatic gzip/brotli compression may have their buffer sizes set too high; you can tune their buffers to be smaller for faster streaming in exchange for slightly worse compression.
14
+
15
+ - Check if “upstream” connections are `keep-alive`: overhead from closing and reopening connections may delay responses.
16
+
17
+ ### NGiNX
18
+
19
+ Most of NGiNX’s relevant parameters are inside [its builtin `http_proxy` module](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffering):
20
+
21
+ ```nginx
22
+ proxy_http_version 1.1; # 1.0 by default
23
+ proxy_buffering off; # on by default
24
+ ```
25
+
26
+ ### Apache
27
+
28
+ Apache’s default configuration works fine with streaming, but your host may have it configured differently. The relevant Apache configuration is inside [its `mod_proxy` and `mod_proxy_*` modules](https://httpd.apache.org/docs/2.4/mod/mod_proxy.html) and their [associated environment variables](https://httpd.apache.org/docs/2.4/env.html).
29
+
30
+ ## CDNs
31
+
32
+ Content Delivery Networks (CDNs) consider efficient streaming one of their best features, but it may be off by default or if certain features are enabled.
33
+
34
+ - For Fastly or another provider that uses VCL configuration, check [if backend responses have `beresp.do_stream = true` set](https://developer.fastly.com/reference/vcl/variables/backend-response/beresp-do-stream/).
35
+
36
+ - Some [Akamai features designed to mitigate slow backends can ironically slow down fast chunked responses](https://community.akamai.com/customers/s/question/0D50f00006n975d/enabling-chunked-transfer-encoding-responses). Try toggling off Adaptive Acceleration, Ion, mPulse, Prefetch, and/or similar performance features. Also check for the following in the configuration:
37
+
38
+ ```html
39
+ <network:http.buffer-response-v2>off</network:http.buffer-response-v2>
40
+ ```
41
+
42
+ ## Node.js itself
43
+
44
+ For extreme cases where [Node streams very small HTML chunks with its built-in compression modules](https://github.com/marko-js/marko/pull/1641), you may need to tweak the compressor stream settings. Here’s an example with `createGzip` and its `Z_PARTIAL_FLUSH` flag:
45
+
46
+ ```js
47
+ const http = require("http");
48
+ const zlib = require("zlib");
49
+
50
+ const markoTemplate = require("./something.marko");
51
+
52
+ http
53
+ .createServer(function (request, response) {
54
+ response.writeHead(200, { "content-type": "text/html;charset=utf-8" });
55
+ const templateStream = markoTemplate.stream({});
56
+ const gzipStream = zlib.createGzip({
57
+ flush: zlib.constants.Z_PARTIAL_FLUSH
58
+ });
59
+ templateStream.pipe(outputStream).pipe(response);
60
+ })
61
+ .listen(80);
62
+ ```
package/docs/vite.md ADDED
@@ -0,0 +1,86 @@
1
+ # Marko + Vite
2
+
3
+ # Installation
4
+
5
+ ```console
6
+ npm install @marko/vite vite
7
+ ```
8
+
9
+ # Example config
10
+
11
+ ```javascript
12
+ import { defineConfig } from "vite";
13
+ import marko from "@marko/vite";
14
+ export default defineConfig({
15
+ plugins: [marko()]
16
+ });
17
+ ```
18
+
19
+ # Linked Mode
20
+
21
+ By default this plugin operates in `linked` mode (you can disabled this by passing [`linked: false` as an option](#optionslinked)). In `linked` mode the plugin automatically discovers all of the entry `.marko` files while compiling the server, and tells `Vite` which modules to load in the browser.
22
+
23
+ With this you _do not_ create `.html` files for `Vite`, it's Marko all the way down!
24
+ Scripts, styles and other content that _would have_ been injected into the `.html` files is instead automatically injected into your `.marko` templates.
25
+
26
+ In this mode you must use the [Vite SSR API](https://vitejs.dev/guide/ssr.html#setting-up-the-dev-server).
27
+
28
+ Here's an example using `express`.
29
+
30
+ ```js
31
+ import { createServer } from "vite";
32
+
33
+ const app = express();
34
+ let loadTemplate;
35
+
36
+ if (process.env.NODE_ENV === "production") {
37
+ // Use Vite's built asset in prod mode.
38
+ loadTemplate = () => import("./dist");
39
+ } else {
40
+ // Hookup the vite dev server.
41
+ const vite = await createViteServer({
42
+ server: { middlewareMode: true }
43
+ });
44
+
45
+ app.use(vite.middlewares);
46
+ loadTemplate = () => vite.ssrLoadModule("./template.marko");
47
+ }
48
+
49
+ app.get("/", async (req, res) => {
50
+ const template = (await loadTemplate()).default;
51
+ // When the template is loaded, it will automaticall have `vite` assets inlined.
52
+ template.render({ hello: "world" }, res);
53
+ );
54
+
55
+ app.listen(3000);
56
+ ```
57
+
58
+ > For a more real world setup check out our [vite express](https://github.com/marko-js/examples/tree/master/examples/vite-express) example app.
59
+
60
+ # Options
61
+
62
+ ### options.babelConfig
63
+
64
+ You can manually override Marko's Babel configuration by passing a `babelConfig` object to the `@marko/vite` plugin. By default Babel's regular [config file resolution](https://babeljs.io/docs/en/config-files) will be used.
65
+
66
+ ```javascript
67
+ marko({
68
+ babelConfig: {
69
+ presets: ["@babel/preset-env"]
70
+ }
71
+ });
72
+ ```
73
+
74
+ ### options.runtimeId
75
+
76
+ In some cases you may want to embed multiple isolated copies of Marko on the page. Since Marko relies on some `window` properties to initialize this can cause issues. For example, by default Marko will read the server rendered hydration code from `window.$components`. In Marko you can change these `window` properties by rendering with `{ $global: { runtimeId: "MY_MARKO_RUNTIME_ID" } }` as input on the server side.
77
+
78
+ This plugin exposes a `runtimeId` option produces output that automatically sets `$global.runtimeId` on the server side and initializes properly in the browser.
79
+
80
+ ```js
81
+ marko({ runtimeId: "MY_MARKO_RUNTIME_ID" });
82
+ ```
83
+
84
+ ### options.linked
85
+
86
+ Set this to `false` to opt out of [linked mode](#linked-mode). When this is false, the plugin will only handle resolving and transforming `.marko` files.
package/docs/webpack.md CHANGED
@@ -1,144 +1,205 @@
1
1
  # Marko + Webpack
2
2
 
3
- The [@marko/webpack/loader](https://github.com/marko-js/webpack) loader for [Webpack](https://webpack.github.io/) will automatically compile all imported Marko templates during bundling. In addition, it will automatically bundle any template dependencies (including required CSS).
3
+ # Installation
4
4
 
5
- > **ProTip**: Want to see it in action? Check out the [`marko-webpack`](https://github.com/marko-js/examples/tree/master/examples/webpack-express) demo repository. Or run `npx @marko/create --template webpack-express` to use this sample as a starting point for a new app.
5
+ > `@marko/webpack` >= 7 Only supports Marko 5+.
6
+ > For Marko 4 support use `@marko/webpack@6`.
6
7
 
7
- ## Installation
8
-
9
- ```
10
- npm install marko
11
- npm install webpack @marko/webpack --save-dev
8
+ ```console
9
+ npm install @marko/webpack
12
10
  ```
13
11
 
14
- ## Client rendering
12
+ ### Loader: `@marko/webpack/loader`
15
13
 
16
- Let's say we have a simple view that we want to render in the browser: `hello.marko`
14
+ The loader portion of this module can be used standalone and simply transforms your Marko templates into the appropriate JavaScript depending on your webpack target.
17
15
 
18
- _hello.marko_
16
+ You can override the output by adding a `target` option to the loader of `target: "server" | "browser"`.
19
17
 
20
- ```marko
21
- <h1>Hello ${input.name}</h1>
22
- ```
18
+ ### Plugin: `@marko/webpack/plugin`
23
19
 
24
- First, let's create a `client.js` that requires the view and renders it to the body:
20
+ The plugin actually creates two separate webpack plugins, the `browser` plugin and the `server` plugin.
25
21
 
26
- _client.js_
22
+ These are intended to be used in a isomorphic [webpack multi compiler](https://github.com/webpack/webpack/tree/master/examples/multi-compiler) where you are bundling both the server and the browser. The way it works is that the server plugin is going to analyze the top level Marko components in your server and automatically communicate with the browser compiler to retrieve the assets for that template.
27
23
 
28
- ```js
29
- import MyTemplate from "./hello.marko";
24
+ This plugin also analyzes the top level Marko templates and determines if it is possible for them to rerender (currently the heuristic is simply does the component have an associated `class` or `component.js`). The plugin will automatically skip sending down any unnecessary top level templates to the browser.
30
25
 
31
- MyTemplate.renderSync({ name: "Marko" }).appendTo(document.body);
32
- ```
26
+ The end result is that you setup a multi compiler (as shown below) and you can simply import Marko templates, and all assets are automatically generated and inlined into an optimized server response. No need to keep track of a webpack manifest yourself!
27
+
28
+ ### Tag: `<webpack-assets>`
33
29
 
34
- Now, let's configure `webpack` to compile the `client.js` file and use `@marko/webpack/loader` for any `*.marko` files:
30
+ The `<webpack-assets>` tag can be used along with the plugin in a multi-compiler setup. This tag allows you to inject `<script>`/`<style>` tags into a server-rendered template for the assets of an entry in the client compiler.
35
31
 
36
- _webpack.config.js_
32
+ #### Example Usage
33
+
34
+ ```marko
35
+ <webpack-assets entry="tracking"/>
36
+ ```
37
+
38
+ #### Example Config
37
39
 
38
40
  ```js
39
- module.exports = {
40
- entry: "./client.js",
41
- output: {
42
- path: __dirname,
43
- filename: "static/bundle.js"
41
+ // ...
42
+ export default [
43
+ {
44
+ entry: "./server.js",
45
+ plugins: [markoPlugin.server]
46
+ // ...
44
47
  },
45
- resolve: {
46
- extensions: [".js", ".marko"]
48
+ {
49
+ // ...
50
+ entry: {
51
+ tracking: "./tracking.js"
52
+ },
53
+ plugins: [markoPlugin.browser]
54
+ }
55
+ ];
56
+ ```
57
+
58
+ # Example
59
+
60
+ ```javascript
61
+ import MarkoPlugin from "@marko/webpack/plugin";
62
+
63
+ const markoPlugin = new MarkoPlugin();
64
+
65
+ export default [
66
+ {
67
+ entry: "./server.js",
68
+ module: {
69
+ rules: [
70
+ {
71
+ test: /\.marko$/,
72
+ loader: "@marko/webpack/loader"
73
+ }
74
+ ]
75
+ },
76
+ plugins: [markoPlugin.server]
47
77
  },
48
- module: {
78
+ {
49
79
  rules: [
50
80
  {
51
81
  test: /\.marko$/,
52
82
  loader: "@marko/webpack/loader"
83
+ },
84
+ // If using `style` blocks with Marko you must use an appropriate loader
85
+ {
86
+ test: /\.css$/,
87
+ use: ["style-loader", "css-loader"]
53
88
  }
54
- ]
89
+ ],
90
+ plugins: [markoPlugin.browser]
55
91
  }
56
- };
92
+ ];
57
93
  ```
58
94
 
59
- Run `webpack` from your terminal and you'll have a new `static/bundle.js` file created. Reference that from an html file and you're good to go.
95
+ ## Babel options (Marko 5+)
96
+
97
+ If you are using Marko 5 with this plugin you can manually override the Babel configuration used by passing a `babelConfig` object along side the `@marko/webpack/loader`. By default Babels regular [config file resolution](https://babeljs.io/docs/en/config-files) will be used.
98
+
99
+ ```javascript
100
+ export default {
101
+ module: {
102
+ rules: [
103
+ {
104
+ test: /\.marko$/,
105
+ loader: "@marko/webpack/loader",
106
+ options: {
107
+ babelConfig: {
108
+ presets: [
109
+ ["@babel/preset-env", { node: "current" }]
110
+ ]
111
+ }
112
+ }
113
+ }
114
+ ]
115
+ }
116
+ },
117
+ ```
60
118
 
61
- _index.html_
119
+ ## Multiple client side compilers
62
120
 
63
- ```html
64
- <!DOCTYPE html>
65
- <html>
66
- <body>
67
- <script src="static/bundle.js"></script>
68
- </body>
69
- </html>
70
- ```
121
+ Sometimes you need to have multiple compilers for your client side bundles. For example with [`i18n`](https://github.com/webpack/webpack/tree/master/examples/i18n) or [even shipping dynamic runtime bundles to the browser](https://github.com/eBay/arc/tree/master/packages/arc-webpack).
71
122
 
72
- Load up that page in your browser and you should see `Hello Marko` staring back at you.
123
+ The Marko webpack browser plugin can be passed to multiple webpack compilers. At runtime you can provide a `$global.buildName` when rendering which will cause assets from the webpack compiler with that name to be included in the page.
73
124
 
74
- ## Using CSS pre-processors
125
+ For example with the webpack i18n plugin you might have a config like the following:
75
126
 
76
- If you're using inline css with pre-processors, you must configure the appropriate loader.
127
+ ```js
128
+ import MarkoPlugin from "@marko/webpack/plugin";
129
+ import I18nPlugin from "i18n-webpack-plugin";
77
130
 
78
- _pretty.marko_
131
+ const languages = {
132
+ en: null,
133
+ de: require("./de.json")
134
+ };
79
135
 
80
- ```marko
81
- style.less {
82
- .pretty {
83
- color:@pretty-color;
84
- }
85
- }
136
+ const markoPlugin = new MarkoPlugin();
137
+
138
+ export default [
139
+ {
140
+ name: "Server",
141
+ entry: "./server.js",
142
+ module: {
143
+ rules: [
144
+ {
145
+ test: /\.marko$/,
146
+ loader: "@marko/webpack/loader"
147
+ }
148
+ ]
149
+ },
150
+ plugins: [markoPlugin.server]
151
+ },
152
+ ...Object.keys(languages).map(language => ({
153
+ name: `Browser-${language}`,
154
+ rules: [
155
+ {
156
+ test: /\.marko$/,
157
+ loader: "@marko/webpack/loader"
158
+ },
159
+ // If using `style` blocks with Marko you must use an appropriate loader
160
+ {
161
+ test: /\.css$/,
162
+ use: ["style-loader", "css-loader"]
163
+ }
164
+ ],
165
+ plugins: [new I18nPlugin(languages[language]), markoPlugin.browser]
166
+ }))
167
+ ];
168
+ ```
169
+
170
+ With the above config you can render your top level Marko template server side with a `$global.buildName`, like so:
86
171
 
87
- <div.pretty/>
172
+ ```javascript
173
+ template.render({ $global: { buildName: "Browser-de" } });
88
174
  ```
89
175
 
90
- _webpack.config.js_
176
+ This will automatically send assets for the German language.
177
+ Of course in this case you'll want to conditionally send the appropriate assets given a users locale. This can be some simply, like so:
91
178
 
92
- ```js
93
- //...
94
- module: {
95
- rules: [
96
- //...
97
- {
98
- test: /\.less$/, // matches style.less { ... } from our template
99
- use: ["style-loader", "css-loader", "less-loader"]
100
- }
101
- //...
102
- ];
103
- }
104
- //...
179
+ ```javascript
180
+ template.render({ $global: { buildName: `Browser-${req.language}` } });
105
181
  ```
106
182
 
107
- ## Extracting CSS
183
+ Note: If a bundle with the provided name does not exist an error will be thrown.
108
184
 
109
- It is recommended to configure the [`MiniCSSExtractPlugin`](https://webpack.js.org/plugins/mini-css-extract-plugin) so that you get a separate css bundle rather than it being included in the JavaScript bundle.
185
+ ## Multiple copies of Marko
110
186
 
111
- ```
112
- npm install mini-css-extract-plugin --save-dev
113
- ```
187
+ In some cases you may want to embed multiple isolated copies of Marko on the page. Since Marko relies on some `window` properties to initialize this can cause issues. For example, by default Marko will read the server rendered hydration code from `window.$components`. In Marko you can change these `window` properties by rendering with `{ $global: { runtimeId: "MY_MARKO_RUNTIME_ID" } }` as input on the server side.
114
188
 
115
- _webpack.config.js_
189
+ This plugin exposes a `runtimeId` option produces output that automatically sets `$global.runtimeId` on the server side and initializes properly in the browser.
190
+ The `runtimeId` will default to the [`uniqueName` option](https://webpack.js.org/configuration/output/#outputuniquename) from the server compiler in the webpack config.
116
191
 
117
192
  ```js
118
- const CSSExtractPlugin = require("mini-css-extract-plugin");
193
+ import MarkoPlugin from "@marko/webpack/plugin";
119
194
 
120
- module.exports = {
121
- entry: "./client.js",
122
- resolve: {
123
- extensions: [".js", ".marko"]
124
- },
125
- module: {
126
- rules: [
127
- {
128
- test: /\.marko$/,
129
- loader: "@marko/webpack/loader"
130
- },
131
- {
132
- test: /\.(less|css)$/,
133
- use: [CSSExtractPlugin.loader, "css-loader", "less-loader"]
134
- }
135
- ]
136
- },
137
- plugins: [
138
- // Write out CSS bundle to its own file:
139
- new CSSExtractPlugin({
140
- filename: "[name].css"
141
- })
142
- ]
143
- };
195
+ const markoPlugin = new MarkoPlugin({
196
+ runtimeId: "MY_MARKO_RUNTIME_ID" // default to webpack `output.uniqueName` option.
197
+ });
144
198
  ```
199
+
200
+ Note: This option will also override the default values for the `jsonpFunction`, `chunkCallbackName` and `hotUpdateFunction` webpack `output` options, which all use global variables, to be prefixed with the `runtimeId`.
201
+
202
+ ## Dynamic public paths
203
+
204
+ When using the plugin, the server will automatically sync the runtime [`__webpack_public_path__`](https://webpack.js.org/guides/public-path/#on-the-fly) with the browser.
205
+ This means that you only need to setup the dynamic public path on the server side.
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "marko",
3
- "version": "5.17.9",
3
+ "version": "5.18.2",
4
4
  "license": "MIT",
5
5
  "description": "UI Components + streaming, async, high performance, HTML templating for Node.js and the browser.",
6
6
  "dependencies": {
7
- "@marko/compiler": "^5.17.6",
8
- "@marko/translator-default": "^5.17.9",
7
+ "@marko/compiler": "^5.18.2",
8
+ "@marko/translator-default": "^5.18.2",
9
9
  "app-module-path": "^2.2.0",
10
10
  "argly": "^1.2.0",
11
11
  "browser-refresh-client": "1.1.4",
@@ -72,5 +72,5 @@
72
72
  "index.js",
73
73
  "node-require.js"
74
74
  ],
75
- "gitHead": "85a4f9922542327b122a94174302f272d2974fb8"
75
+ "gitHead": "65999cf2accd091c1455705b94b91ca1ed64203f"
76
76
  }