marko 5.22.2 → 5.22.4

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -1,84 +1,75 @@
1
- <h1 align="center">
2
- <a href="https://markojs.com/"><img src="https://raw.githubusercontent.com/marko-js/branding/master/marko-logo-medium-cropped.png" alt="Marko" width="250" /></a>
3
- </h1>
1
+ <div align="center">
4
2
 
5
- <p align="center">
6
- <strong>A declarative, HTML-based language that makes building web apps fun 🔥</strong>
7
- </p>
3
+ # [<img alt="Marko" src="https://raw.githubusercontent.com/marko-js/branding/master/marko-logo-medium-cropped.png" width="250">](https://markojs.com/)
8
4
 
9
- <p align="center">
10
- <a href="https://www.npmjs.com/package/marko"><img alt="NPM" src="https://img.shields.io/npm/v/marko.svg"/></a>
11
- <a href="https://discord.gg/RFGxYGs"><img alt="Discord" src="https://img.shields.io/badge/discord-chat-7188da.svg"/></a>
12
- <a href="https://github.com/marko-js/marko/actions/workflows/ci.yml">
13
- <img src="https://github.com/marko-js/marko/actions/workflows/ci.yml/badge.svg" alt="Build status"/>
14
- </a>
15
- <a href="https://codecov.io/gh/marko-js/marko"><img alt="Coverage Status" src="https://codecov.io/gh/marko-js/marko/branch/master/graph/badge.svg"/></a>
16
- <a href="http://npm-stat.com/charts.html?package=marko"><img alt="Downloads" src="https://img.shields.io/npm/dm/marko.svg"/></a>
17
- </p>
5
+ **A declarative, HTML-based language that makes building web apps fun 🔥**
18
6
 
19
- <p align="center">
20
- <a href="https://markojs.com/docs/getting-started/">Docs</a> ∙ <a href="https://markojs.com/try-online/">Try Online</a> ∙ <a href="#contributors">Contribute</a> ∙ <a href="#community--support">Get Support</a>
21
- </p>
7
+ [![NPM](https://img.shields.io/npm/v/marko.svg)](https://www.npmjs.com/package/marko)
8
+ [![Discord Chat](https://img.shields.io/badge/discord-chat-7188da.svg)](https://discord.gg/RFGxYGs)
9
+ [![Continuous Integration status](https://github.com/marko-js/marko/actions/workflows/ci.yml/badge.svg)](https://github.com/marko-js/marko/actions/workflows/ci.yml)
10
+ [![Code coverage %](https://codecov.io/gh/marko-js/marko/branch/master/graph/badge.svg)](https://codecov.io/gh/marko-js/marko)
11
+ [![# of monthly downloads](https://img.shields.io/npm/dm/marko.svg)](https://npm-stat.com/charts.html?package=marko)
22
12
 
23
- # Intro
13
+ [Docs](https://markojs.com/docs/getting-started/) ∙ [Try Online](https://markojs.com/try-online/) ∙ [Contribute](#contributors) ∙ [Get Support](#community--support)
24
14
 
25
- Marko is HTML _re-imagined_ as a language for building dynamic and reactive user interfaces.
26
- Just about any valid HTML is valid Marko, but Marko extends the HTML language to allow
27
- building modern applications in a declarative way.
15
+ </div>
16
+
17
+ ## Intro
28
18
 
29
- Among these extensions are [conditionals](https://markojs.com/docs/conditionals-and-lists/#conditionals), [lists](https://markojs.com/docs/conditionals-and-lists/#lists), [state](https://markojs.com/docs/state/), and [components](https://markojs.com/docs/class-components/).
30
- Marko supports both single-file components and components broken into separate files.
19
+ Marko is HTML _reimagined_ as a language for building dynamic and reactive user interfaces. Almost any valid HTML is valid Marko, and Marko extends HTML for building modern applications more declaratively. Among these extensions are [conditionals and lists](https://markojs.com/docs/conditionals-and-lists/), [state](https://markojs.com/docs/state/), and [components](https://markojs.com/docs/class-components/).
31
20
 
32
- ## Single file component
21
+ Marko supports both single-file components and components across separate files.
33
22
 
34
- The following single-file component renders a button and a counter with the
35
- number of times the button has been clicked.
23
+ ### Single-file component
24
+
25
+ The following renders a button and a counter of how many times the button has been pressed:
36
26
 
37
27
  **click-count.marko**
38
28
 
39
29
  ```marko
40
30
  class {
41
- onCreate() {
42
- this.state = { count: 0 };
43
- }
44
- increment() {
45
- this.state.count++;
46
- }
31
+ onCreate() {
32
+ this.state = { count: 0 };
33
+ }
34
+ increment() {
35
+ this.state.count++;
36
+ }
47
37
  }
48
38
 
49
39
  style {
50
- .count {
51
- color: #09c;
52
- font-size: 3em;
53
- }
54
- .example-button {
55
- font-size: 1em;
56
- padding: 0.5em;
57
- }
40
+ .count {
41
+ color: #09c;
42
+ font-size: 3em;
43
+ }
44
+ .press-me {
45
+ padding: 0.5em;
46
+ }
58
47
  }
59
48
 
60
- <div.count>
61
- ${state.count}
62
- </div>
63
- <button.example-button on-click('increment')>
64
- Click me!
49
+ <output.count>
50
+ ${state.count}
51
+ </output>
52
+ <button.press-me on-click('increment')>
53
+ Press me!
65
54
  </button>
66
55
  ```
67
56
 
68
- ## Multi-file component
57
+ ### Multi-file component
58
+
59
+ The same component as above, but split into:
69
60
 
70
- The same component as above split into an `index.marko` template file,
71
- `component.js` containing your component logic, and `style.css` containing your
72
- component style:
61
+ - `index.marko` template file
62
+ - `component.js` component JS logic file
63
+ - `style.css` component styles file
73
64
 
74
65
  **index.marko**
75
66
 
76
67
  ```marko
77
- <div.count>
78
- ${state.count}
79
- </div>
80
- <button.example-button on-click('increment')>
81
- Click me!
68
+ <output.count>
69
+ ${state.count}
70
+ </output>
71
+ <button.press-me on-click('increment')>
72
+ Press me!
82
73
  </button>
83
74
  ```
84
75
 
@@ -102,59 +93,68 @@ export default {
102
93
  color: #09c;
103
94
  font-size: 3em;
104
95
  }
105
- .example-button {
106
- font-size: 1em;
96
+ .press-me {
107
97
  padding: 0.5em;
108
98
  }
109
99
  ```
110
100
 
111
101
  ## Concise Syntax
112
102
 
113
- Marko also supports a beautifully concise syntax as an alternative to its HTML
114
- syntax. Find out more about the [concise syntax here](https://markojs.com/docs/concise/).
103
+ Marko also supports [a beautifully concise syntax as an alternative](https://markojs.com/docs/concise/) to its HTML syntax:
104
+
105
+ <table><thead><tr><th>Concise syntax<th>HTML syntax
106
+ <tbody><tr>
107
+ <td>
115
108
 
116
109
  ```marko
117
- <!-- Marko HTML syntax -->
118
- <ul class="example-list">
119
- <for|color| of=['a', 'b', 'c']>
120
- <li>${color}</li>
121
- </for>
122
- </ul>
110
+ ul.example-list
111
+ for|color| of=[a, b, c]
112
+ li -- ${color}
123
113
  ```
124
114
 
115
+ <td>
116
+
125
117
  ```marko
126
- // Marko concise syntax
127
- ul.example-list
128
- for|color| of=['a', 'b', 'c']
129
- li -- ${color}
118
+ <ul class="example-list">
119
+ <for|color| of=[a, b, c]>
120
+ <li>${color}</li>
121
+ </for>
122
+ </ul>
130
123
  ```
131
124
 
132
- # Getting Started
125
+ </table>
126
+
127
+ ## Getting Started
133
128
 
134
129
  1. `npm install marko`
135
130
  2. Read the [docs](https://markojs.com/docs/getting-started/)
136
131
 
137
- # Community & Support
132
+ ## Community & Support
138
133
 
139
- | <a alt="See Marko questions on Stack Overflow" href="https://stackoverflow.com/questions/tagged/marko"><img src="https://user-images.githubusercontent.com/1958812/56055468-619b3e00-5d0e-11e9-92ae-200c212cafb8.png" height="50px"/></a> | <a alt="Chat with us on Discord" href="https://discord.gg/RFGxYGs"><img src="https://user-images.githubusercontent.com/4985201/89313514-6edbea80-d62d-11ea-8447-ca2fd8983661.png" height="55px"/></a> | <a alt="Tweet about Marko" href="https://twitter.com/intent/tweet?hashtags=markojs"><img src="https://user-images.githubusercontent.com/1958812/56055707-07e74380-5d0f-11e9-8a59-d529fbb5a81e.png" height="40px"/></a> |
140
- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
141
- | Ask and answer StackOverflow questions with the [`marko` tag](https://stackoverflow.com/questions/tagged/marko) | Come hang out in our Discord chat room, ask questions, and discuss project direction | Tweet to [`@MarkoDevTeam`](https://twitter.com/MarkoDevTeam) or with the [`#markojs` hashtag](https://twitter.com/search?q=%23markojs&f=live) |
134
+ <table>
135
+ <thead><tr>
136
+ <th><img alt="Stack Overflow" src="https://user-images.githubusercontent.com/1958812/56055468-619b3e00-5d0e-11e9-92ae-200c212cafb8.png" width="205">
137
+ <th><img alt="Discord" src="https://user-images.githubusercontent.com/4985201/89313514-6edbea80-d62d-11ea-8447-ca2fd8983661.png" width="162">
138
+ <th><img alt="Twitter" src="https://user-images.githubusercontent.com/1958812/56055707-07e74380-5d0f-11e9-8a59-d529fbb5a81e.png" width="53">
139
+ <tbody><tr><td>
140
+
141
+ Ask and answer [StackOverflow questions with the `#marko` tag](https://stackoverflow.com/questions/tagged/marko)<td>
142
142
 
143
- # Contributors
143
+ Come [hang out in our Discord chat](https://discord.gg/RFGxYGs), ask questions, and discuss project direction<td>
144
144
 
145
- Marko would not be what it is without all those who have contributed ✨
145
+ [Tweet to `@MarkoDevTeam`](https://twitter.com/MarkoDevTeam), or with the [`#markojs` hashtag](https://twitter.com/search?q=%23markojs&f=live)
146
146
 
147
- <a href="https://github.com/marko-js/marko/graphs/contributors">
148
- <img src="https://opencollective.com/marko-js/contributors.svg?width=890&button=false"/>
149
- </a>
147
+ </table>
150
148
 
151
- ## Get Involved!
149
+ ### Contributors
152
150
 
153
- - Pull requests are welcome!
154
- - Read [`CONTRIBUTING.md`](.github/CONTRIBUTING.md) and check out our [bite-sized](https://github.com/marko-js/marko/issues?q=is%3Aissue+is%3Aopen+label%3Adifficulty%3Abite-sized) and [help-wanted](https://github.com/marko-js/marko/issues?q=is%3Aissue+is%3Aopen+label%3Astatus%3Ahelp-wanted) issues
155
- - Submit github issues for any feature enhancements, bugs or documentation problems
156
- - By participating in this project you agree to abide by its [Code of Conduct](https://ebay.github.io/codeofconduct).
151
+ Marko would not be what it is without all those who have contributed ✨
152
+
153
+ [![All marko-js/marko GitHub contributors](https://opencollective.com/marko-js/contributors.svg?width=890&button=false)](https://github.com/marko-js/marko/graphs/contributors)
157
154
 
158
- # License
155
+ ### Get Involved!
159
156
 
160
- MIT
157
+ - Pull requests are welcome!
158
+ - Submit [GitHub issues](https://github.com/marko-js/marko/issues) for any feature enhancements, bugs, or documentation problems
159
+ - [Read the Contribution Tips and Guidelines](.github/CONTRIBUTING.md)
160
+ - Participants in this project agree to abide by [its Code of Conduct](https://github.com/eBay/.github/blob/main/CODE_OF_CONDUCT.md)
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @template T
3
+ * @typedef {{
4
+ * value?: T;
5
+ * then?: Marko.Body<[Awaited<T>], void>;
6
+ * catch?: Marko.Body<[unknown], void>;
7
+ * placeholder?: Marko.Body<[], void>;
8
+ * client-reorder?: boolean;
9
+ * name?: string;
10
+ * timeout?: number;
11
+ * show-after?: string;
12
+ * }} Input
13
+ */
package/docs/rollup.md CHANGED
@@ -1,155 +1,309 @@
1
1
  # Marko + Rollup
2
2
 
3
- # Installation
3
+ This is Marko’s official integration plugin for [the Rollup bundler](https://rollupjs.org/).
4
4
 
5
- ```console
6
- npm install @marko/rollup rollup @rollup/plugin-node-resolve @rollup/plugin-commonjs -D
5
+ ## Installation
6
+
7
+ ```sh
8
+ npm install --save-dev \
9
+ @marko/rollup \
10
+ rollup \
11
+ @rollup/plugin-node-resolve \
12
+ @rollup/plugin-commonjs
7
13
  ```
8
14
 
9
- # Basic example config
15
+ > **Note**: The Marko runtime is CommonJS, so don’t forget the `@rollup/plugin-commonjs` package!
16
+
17
+ ## Configuration
18
+
19
+ `@marko/rollup` exports two methods for use in [Rollup configuration files](https://rollupjs.org/guide/en/#configuration-files): `.browser()` and `.server()`.
20
+
21
+ You _probably_ want to use both, since that’ll get you…
22
+
23
+ - Automatic [`input` entrypoint configuration](https://rollupjs.org/guide/en/#input) for route-based bundle splitting
24
+ - Complete control over asset loading with [the `<rollup>` tag](#rollup-tag)
25
+ - The strengths behind why Marko exists in the first place: cooperation between servers and browsers for high performance in both
26
+
27
+ > **ProTip**: You _could_ use only `.browser()` or only `.server()` to build a completely client-side-rendered or server-side-rendered app. That would be a little odd, but you could.
10
28
 
11
- **Note: The Marko runtime is authored in commonjs, this means the `@rollup/plugin-commonjs` is required!**
29
+ ### Config example
12
30
 
13
- ```javascript
31
+ ```js
14
32
  import nodeResolve from "@rollup/plugin-node-resolve";
15
33
  import commonjs from "@rollup/plugin-commonjs";
16
34
  import marko from "@marko/rollup";
17
35
 
18
- export default {
19
- ...,
36
+ const sharedPlugins = [
37
+ commonjs({
38
+ extensions: [".js", ".marko"]
39
+ }),
40
+ // If using Marko’s `style {}` blocks, you’ll need an appropriate plugin, like npmjs.com/rollup-plugin-postcss
41
+ postcss({ external: true })
42
+ ]
43
+
44
+ const serverAssetsConfig = {
45
+ input: "src/start-server.js",
46
+ plugins: [
47
+ marko.server(),
48
+ nodeResolve({ preferBuiltins: true })
49
+ ...sharedPlugins
50
+ ]
51
+ };
52
+ const browsersAssetsConfig = {
20
53
  plugins: [
21
54
  marko.browser(),
22
- nodeResolve({
23
- browser: true,
24
- extensions: [".js", ".marko"]
25
- }),
26
- // NOTE: The Marko runtime uses commonjs so this plugin is also required.
27
- commonjs({
28
- extensions: [".js", ".marko"]
29
- }),
30
- // If using `style` blocks with Marko you must use an appropriate plugin.
31
- postcss({
32
- external: true
33
- })
55
+ nodeResolve({ browser: true })
56
+ ...sharedPlugins
34
57
  ]
35
58
  };
59
+
60
+ export default [serverAssetsConfig, browsersAssetsConfig];
36
61
  ```
37
62
 
38
- Likewise, if bundling the components for the server use `marko.server()` as the plugin.
63
+ ### Advanced config example
39
64
 
40
- # Linked config
65
+ The following configuration file is long and hairy, which may be upsetting to some viewers. However, it does show how to accomplish the following:
41
66
 
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.
67
+ - Support for Rollup’s watch mode
68
+ - A bundle analyzer
69
+ - The ability to `import` JSON files to use their data
70
+ - The ability to `import` image files to use their asset URLs for `img[src]` and such
71
+ - Dead-code elimination for development-only code
72
+ - Static compression of assets for something like [NGiNX’s `gzip_static`](https://nginx.org/en/docs/http/ngx_http_gzip_static_module.html)
73
+ - A CSS preprocessor (Sass, in this case)
74
+ - Browserslist to automatically configure:
75
+ - Babel for JS transpilation
76
+ - Autoprefixer for CSS transpilation
45
77
 
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
- }];
78
+ <details><summary>Big ugly production-esque Rollup config</summary>
79
+
80
+ ```js
81
+ import { builtinModules } from "module";
82
+ import path from "path";
83
+ import autoprefixer from "autoprefixer";
84
+ import babelPlugin from "@rollup/plugin-babel";
85
+ import commonjsPlugin from "@rollup/plugin-commonjs";
86
+ import jsonPlugin from "@rollup/plugin-json";
87
+ import markoPlugin from "@marko/rollup";
88
+ import nodeResolvePlugin from "@rollup/plugin-node-resolve";
89
+ import replacePlugin from "@rollup/plugin-replace";
90
+ import runPlugin from "@rollup/plugin-run";
91
+ import stylesPlugin from "rollup-plugin-styles";
92
+ import urlPlugin from "@rollup/plugin-url";
93
+ import pkg from "./package.json";
94
+
95
+ const __DEV__ = process.env.NODE_ENV === "development";
96
+ const __PROD__ = !__DEV__;
97
+
98
+ const isWatch = Boolean(process.env.ROLLUP_WATCH);
99
+
100
+ const publicPath = "/s/"; // Guess what character is only 5 bits under HPACK
101
+ const assetFileNames = "[name]-[hash][extname]";
102
+
103
+ const externalDependencies = [
104
+ ...Object.keys(pkg.dependencies),
105
+ ...builtinModules
106
+ ];
107
+
108
+ process.env.SASS_PATH = "./:./node_modules";
109
+
110
+ export default (async () => [
111
+ compiler("server", {
112
+ input: "index.js",
113
+ output: {
114
+ dir: "built/server/",
115
+ assetFileNames: `../browser/${assetFileNames}`,
116
+ format: "cjs",
117
+ sourcemap: true
118
+ },
119
+ external: id =>
120
+ externalDependencies.some(
121
+ dependency => id === dependency || id.startsWith(dependency + "/")
122
+ ),
123
+ plugins: [isWatch && runPlugin({ execArgv: ["--enable-source-maps"] })]
124
+ }),
125
+
126
+ compiler("browser", {
127
+ output: {
128
+ dir: "built/browser/",
129
+ chunkFileNames: __PROD__ ? "[name]-[hash].js" : null,
130
+ entryFileNames: __PROD__ ? "[name]-[hash].js" : null,
131
+ assetFileNames,
132
+ sourcemap: true,
133
+ sourcemapExcludeSources: __PROD__
134
+ },
135
+ plugins: [
136
+ stylesPlugin({
137
+ mode: "extract",
138
+ sourceMap: true,
139
+ config: {
140
+ target: "browserslist:css",
141
+ plugins: [autoprefixer({ env: "css" })]
142
+ },
143
+ minimize: __PROD__,
144
+ url: {
145
+ publicPath,
146
+ hash: assetFileNames
147
+ }
148
+ }),
149
+ __PROD__ && (await import("rollup-plugin-terser")).terser(),
150
+ __PROD__ &&
151
+ (await import("rollup-plugin-gzip")).default({
152
+ filter: /\.(?:js|css|svg|json|xml|txt)$/,
153
+ minSize: 1024,
154
+ gzipOptions: {
155
+ level: 9,
156
+ memLevel: 9
157
+ }
158
+ }),
159
+ __PROD__ &&
160
+ !isWatch &&
161
+ (await import("rollup-plugin-visualizer")).default(),
162
+ __PROD__ &&
163
+ !isWatch && {
164
+ name: "bundle-visualizer-location",
165
+ writeBundle() {
166
+ console.info(
167
+ `📊 Bundle visualizer at \x1b[4;36mfile://${path.join(
168
+ __dirname,
169
+ "../../",
170
+ bundleAnalyzerFilename
171
+ )}\x1b[0m`
172
+ );
173
+ }
174
+ }
175
+ ]
176
+ })
177
+ ])();
178
+
179
+ function compiler(target, config) {
180
+ const isBrowser = target === "browser";
181
+ const browserslistEnv = isBrowser ? "js" : "server";
182
+ const babelConfig = {
183
+ comments: false,
184
+ browserslistEnv,
185
+ compact: false,
186
+ babelrc: false,
187
+ caller: { target }
188
+ };
189
+ if (isBrowser) {
190
+ babelConfig.presets = [
191
+ [
192
+ "@babel/preset-env",
193
+ {
194
+ browserslistEnv,
195
+ bugfixes: true
196
+ }
197
+ ]
198
+ ];
199
+ }
200
+
201
+ return {
202
+ ...config,
203
+ preserveEntrySignatures: false,
204
+ plugins: [
205
+ markoPlugin[target]({ babelConfig }),
206
+ nodeResolvePlugin({
207
+ browser: isBrowser,
208
+ preferBuiltins: !isBrowser
209
+ }),
210
+ commonjsPlugin(),
211
+ replacePlugin({
212
+ preventAssignment: true,
213
+ values: { __DEV__, __PROD__ }
214
+ }),
215
+ babelPlugin({
216
+ babelHelpers: "bundled",
217
+ ...babelConfig
218
+ }),
219
+ jsonPlugin(),
220
+ urlPlugin({
221
+ publicPath,
222
+ destDir: "built/browser/",
223
+ fileName: assetFileNames,
224
+ include: "**/*.{svg,png,jpg,jpeg}",
225
+ limit: 0, // Never Base64 & inline
226
+ emitFiles: !isBrowser
227
+ }),
228
+ ...config.plugins
229
+ ]
230
+ };
231
+ }
61
232
  ```
62
233
 
234
+ </details>
235
+
63
236
  ## `<rollup>` tag
64
237
 
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.
238
+ Using both `.server()` and `.browser()` enables **the `<rollup>` tag**, which gives you complete control over how your app loads assets. That lets you do things like:
66
239
 
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.
240
+ - [Critical CSS](https://web.dev/extract-critical-css/) for components as they write out within a kB budget, or [as components first appear on the page](https://jakearchibald.com/2016/link-in-body/#a-simpler-better-way), or any other style-loading mad science
241
+ - [`module`/`nomodule` scripts](https://philipwalton.com/articles/using-native-javascript-modules-in-production-today/) for smaller bundles in modern browsers
242
+ - [Content-Security Policy `nonce`s](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) or [Subresource `integrity` hashes](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity)
243
+ - Anything a web page can do, really. You can even combine `<rollup>` with [the `serialize` option](#options.serialize) to be as fancy as you wanna be.
69
244
 
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.
245
+ The `<rollup>` tag provides two [tag parameters](https://markojs.com/docs/syntax/#parameters):
246
+
247
+ 1. `entry` is the generated `input` string that the `server` plugin gave to the `browser` plugin. You can use it to find the corresponding entry chunk from Rollup’s `output` (the next parameter).
248
+
249
+ 2. `output` is an array of `AssetInfo | ChunkInfo` objects with most of [the data returned from Rollup's `generateBundle` hook](https://rollupjs.org/guide/en/#generatebundle). Some properties are omitted, like `code` and `map`, since they’re often too large to inline directly. However, each chunk also has a `size` property, to let you filter out empty chunks, inline code yourself below a certain size, or other delightful devilishness.
250
+
251
+ For example, using the `entry` name and properties of `output` items to load scripts:
71
252
 
72
253
  ```marko
73
254
  <head>
74
255
  <rollup|entry, output|>
75
256
  $ const entryChunk = output.find(chunk => chunk.name === entry);
76
257
 
77
- <if(entryChunk.size /* skip scripts all together if empty js file */)>
258
+ <if(entryChunk.size /* only load non-empty JS entry points */)>
78
259
  <for|fileName| of=entryChunk.imports>
79
- <link rel="modulepreload" href=fileName/>
260
+ <link rel="modulepreload" href=fileName />
80
261
  </for>
81
262
 
82
- <script async type="module" src=entryChunk.fileName/>
263
+ <script async type="module" src=entryChunk.fileName></script>
83
264
  </if>
84
265
  </rollup>
85
266
  </head>
86
267
  ```
87
268
 
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.
269
+ > **Note**: It’s up to you to transform the chunk data (also called the **manifest**) into `<link>`s, `<script>`s, and other HTML to load assets. Opting into complete control means we can’t do any of it for you.
89
270
 
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.
271
+ If your Rollup `browser` config contains multiple `output` options, or you have multiple `browser` configs, every `output`’s chunk is passed to the `<rollup>` tag.
91
272
 
92
- For example if you have an `esm` and `iife` build:
273
+ For example, if you have both `esm` and `iife` build outputs configured:
93
274
 
94
- ```javascript
275
+ ```js
95
276
  {
96
- plugins: [
97
- marko.browser()
98
- ...
99
- ],
100
277
  output: [
101
- { dir: 'dist/iife', format: 'iife' },
102
- { dir: 'dist/esm', format: 'esm' }
103
- ]
278
+ { dir: "dist/iife", format: "iife" },
279
+ { dir: "dist/esm", format: "esm" }
280
+ ];
104
281
  }
105
282
  ```
106
283
 
107
- we could access the assets from both builds:
284
+ …you could cross-reference assets from both
108
285
 
109
286
  ```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);
287
+ <rollup|entry, iifeOutput, esmOutput|>
288
+ $ const iifeEntryChunk = iifeOutput.find(chunk => chunk.name === entry);
289
+ $ const esmEntryChunk = esmOutput.find(chunk => chunk.name === entry);
114
290
 
115
- <script async type="module" src=esmEntryChunk.fileName/>
116
- <script nomodule src=iifeEntryChunk.fileName></script>
117
- </rollup>
118
- </head>
291
+ <script src=esmEntryChunk.fileName type="module" async></script>
292
+ <script src=iifeEntryChunk.fileName nomodule async></script>
293
+ </rollup>
119
294
  ```
120
295
 
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
- ```
296
+ and _boom:_ you now have [a `module`/`nomodule` setup](https://philipwalton.com/articles/using-native-javascript-modules-in-production-today/).
143
297
 
144
298
  ## Options
145
299
 
146
- Both the `server` and `browser` plugins can receive the same options.
300
+ ### `options.babelConfig`
147
301
 
148
- ### options.babelConfig
302
+ Both the `.server()` and `.browser()` plugins accept this option.
149
303
 
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.
304
+ You can manually override the builtin Babel configuration by passing a `babelConfig` object. By default, [Babel’s regular config file resolution](https://babeljs.io/docs/en/config-files) will be used.
151
305
 
152
- ```javascript
306
+ ```js
153
307
  marko.browser({
154
308
  babelConfig: {
155
309
  presets: ["@babel/preset-env"]
@@ -157,42 +311,40 @@ marko.browser({
157
311
  });
158
312
  ```
159
313
 
160
- ### options.runtimeId
314
+ ### `options.runtimeId`
315
+
316
+ Both the `.server()` and `.browser()` plugins accept this option. In fact, you _really_ want to use it with both simultaneously.
161
317
 
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.
318
+ In some cases, you may want to embed multiple isolated copies of Marko on the page. (If you can’t think of why, then don’t worry about this option.)
163
319
 
164
- This plugin exposes a `runtimeId` option produces output that automatically sets `$global.runtimeId` on the server side and initializes properly in the browser.
320
+ Since Marko uses some `window` properties to initialize, multiple instances can cause issues. For example, by default Marko checks `window.$components` for server-rendered hydration. Usually you can change these `window` properties by [rendering with `{ $global: { runtimeId: "MY_MARKO_RUNTIME_ID" } }` as input](https://markojs.com/docs/rendering/#global-data) on the server, but since `@marko/rollup` usually writes the autoinitialization code for you, instead this plugin exposes a `runtimeId` option to automatically set `$global.runtimeId` to initialize properly in the browser:
165
321
 
166
322
  ```js
167
323
  const runtimeId = "MY_MARKO_RUNTIME_ID";
168
- // Make sure the `runtimeId` is the same across all of your plugins!
324
+ // Make the `runtimeId` the same across `server` and `browser`, or it’ll error!
169
325
  marko.server({ runtimeId });
170
326
  marko.browser({ runtimeId });
171
327
  ```
172
328
 
173
- ### options.serialize
329
+ ### `options.serialize`
174
330
 
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:
331
+ This option is only available for the `.browser()` plugin. It lets you inspect and transform the `output` chunks before they’re passed to [the `<rollup>` tag](#rollup-tag).
332
+
333
+ For example, if you _did_ want to include the `code` property from the Rollup chunk — say, to inline code small enough that it’s not worth the overhead of an HTTP request, you’d try something like the following:
177
334
 
178
335
  ```js
179
336
  marko.browser({
180
337
  serialize(output) {
181
- return output.map(chunk =>
182
- chunk.type === "asset"
183
- ? {
184
- type: "asset",
185
- fileName: chunk.fileName
186
- }
338
+ return output.map(({ type, fileName, isEntry, code }) =>
339
+ type === "asset"
340
+ ? { type, fileName }
187
341
  : {
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
342
+ type,
343
+ name,
344
+ isEntry,
345
+ fileName,
346
+ // only inline code chunks below 1kB
347
+ inline: code.trim().length < 1024 && code
196
348
  }
197
349
  );
198
350
  }
@@ -10,10 +10,16 @@
10
10
 
11
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
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
13
  - Check if “upstream” connections are `keep-alive`: overhead from closing and reopening connections may delay responses.
16
14
 
15
+ - For typical modern webpage filesizes, the following bullet points probably won’t matter. But if you want to stream **small chunks of data with the lowest latency**, investigate these sources of buffering:
16
+
17
+ - 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.
18
+
19
+ - You can [tune HTTPS record sizes for lower latency, as described in High Performance Browser Networking](https://hpbn.co/transport-layer-security-tls/#optimize-tls-record-size).
20
+
21
+ - Turning off MIME sniffing with [the `X-Content-Type-Options`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options) header eliminates browser buffering at the very beginning of HTTP responses
22
+
17
23
  ### NGiNX
18
24
 
19
25
  Most of NGiNX’s relevant parameters are inside [its builtin `http_proxy` module](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffering):
package/index.d.ts ADDED
@@ -0,0 +1,317 @@
1
+ declare module "*.marko" {
2
+ const template: Marko.Template;
3
+ export default template;
4
+ }
5
+
6
+ declare namespace NodeJS {
7
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
8
+ interface ReadableStream {}
9
+ }
10
+
11
+ declare namespace Marko {
12
+ /** A mutable global object for the current render. */
13
+ export interface Global {
14
+ serializedGlobals?: Record<string, boolean>;
15
+ [attr: PropertyKey]: unknown;
16
+ }
17
+
18
+ export type TemplateInput<Input = { [attr: PropertyKey]: any }> = Input & {
19
+ $global?: Global;
20
+ };
21
+
22
+ export interface Out<Component extends Marko.Component = Marko.Component>
23
+ extends PromiseLike<RenderResult<Component>> {
24
+ /** The underlying ReadableStream Marko is writing into. */
25
+ stream: unknown;
26
+ /** A mutable global object for the current render. */
27
+ global: Global;
28
+ /** Disable all async rendering. Will error if something beings async. */
29
+ sync(): void;
30
+ /** Returns true if async rendering is disabled. */
31
+ isSync(): boolean;
32
+ /** Write unescaped text at the current stream position. */
33
+ write(val: string | void): this;
34
+ /** Write javascript content to be merged with the scripts Marko sends out on the next flush. */
35
+ script(val: string | void): this;
36
+ /** Returns the currently rendered html content. */
37
+ toString(): string;
38
+ /** Starts a new async/forked stream. */
39
+ beginAsync(options?: {
40
+ name?: string;
41
+ timeout?: number;
42
+ last?: boolean;
43
+ }): Out;
44
+ /** Marks the current stream as complete (async streams may still be executing). */
45
+ end(val?: string | void): this;
46
+ emit(eventName: PropertyKey, ...args: any[]): boolean;
47
+ on(eventName: PropertyKey, listener: (...args: any[]) => any): this;
48
+ once(eventName: PropertyKey, listener: (...args: any[]) => any): this;
49
+ prependListener(
50
+ eventName: PropertyKey,
51
+ listener: (...args: any[]) => any
52
+ ): this;
53
+ removeListener(
54
+ eventName: PropertyKey,
55
+ listener: (...args: any[]) => any
56
+ ): this;
57
+ /** Register a callback executed when the last async out has completed. */
58
+ onLast(listener: (next: () => void) => unknown): this;
59
+ /** Pipe Marko's stream to another stream. */
60
+ pipe(stream: unknown): this;
61
+ /** Emits an error on the stream. */
62
+ error(e: Error): this;
63
+ /** Schedules a Marko to flush buffered html to the underlying stream. */
64
+ flush(): this;
65
+ /** Creates a detached out stream (used for out of order flushing). */
66
+ createOut(): Out;
67
+ /** Write escaped text at the current stream position. */
68
+ text(val: string | void): void;
69
+ }
70
+
71
+ /** Body content created from by a component, typically held in an object with a renderBody property. */
72
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
73
+ export interface Body<
74
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
75
+ in Params extends readonly any[] = [],
76
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
77
+ out Return = void
78
+ > {}
79
+
80
+ /** Valid data types which can be passed in as a <${dynamic}/> tag name. */
81
+ export type DynamicTagName =
82
+ | {
83
+ renderBody?: Body<any, any> | Template | string | void | false;
84
+ }
85
+ | Body<any, any>
86
+ | Template
87
+ | string
88
+ | void
89
+ | false;
90
+
91
+ /** Extract the return tag type from a renderBody. */
92
+ export type BodyReturnType<B> = B extends Body<any, infer Return>
93
+ ? Return
94
+ : never;
95
+
96
+ /** Extract the tag parameter types received by a renderBody. */
97
+ export type BodyParamaters<B> = B extends Body<infer Params, any>
98
+ ? Params
99
+ : never;
100
+
101
+ export abstract class Component<
102
+ Input extends Record<PropertyKey, any> = Record<PropertyKey, any>
103
+ > implements Emitter
104
+ {
105
+ /** A unique id for this instance. */
106
+ public readonly id: string;
107
+ /** The top level element rendered by this instance. */
108
+ public readonly el: Element | void;
109
+ /** The attributes passed to this instance. */
110
+ public readonly input: Input;
111
+ /** @deprecated */
112
+ public readonly els: Element[];
113
+ /** Mutable state that when changed causes a rerender. */
114
+ abstract state: undefined | null | Record<PropertyKey, any>;
115
+
116
+ /** Returns the amount of event handlers listening to a specific event. */
117
+ listenerCount(eventName: PropertyKey): number;
118
+ /**
119
+ * Used to wrap an existing event emitted and ensure that all events are
120
+ * cleaned up once this component is destroyed
121
+ * */
122
+ subscribeTo(
123
+ emitter: unknown
124
+ ): Omit<Emitter, "listenerCount" | "prependListener" | "emit">;
125
+ /** Emits an event on the component instance. */
126
+ emit(eventName: PropertyKey, ...args: any[]): boolean;
127
+ /** Listen to an event on the component instance. */
128
+ on(eventName: PropertyKey, listener: (...args: any[]) => any): this;
129
+ /** Listen to an event on the component instance once. */
130
+ once(eventName: PropertyKey, listener: (...args: any[]) => any): this;
131
+ /** Listen to an event on the component instance before all other listeners. */
132
+ prependListener(
133
+ eventName: PropertyKey,
134
+ listener: (...args: any[]) => any
135
+ ): this;
136
+ /** Remove a listener from the component instance. */
137
+ removeListener(
138
+ eventName: PropertyKey,
139
+ listener: (...args: any[]) => any
140
+ ): this;
141
+ /** Remove all listeners from the component instance. */
142
+ removeAllListeners(eventName?: PropertyKey): this;
143
+ /** Removes the component instance from the DOM and cleans up all active event handlers including all children. */
144
+ destroy(): void;
145
+ /** Schedule an update (similar to if a state had been changed). */
146
+ forceUpdate(): void;
147
+ /** Generates a unique id derived from the current unique instance id (similar to :scoped in the template). */
148
+ elId(key?: string, index?: number): string;
149
+ /** @alias elId */
150
+ getElId(key?: string, index?: number): string;
151
+ /** Gets an element reference by its `key` attribute in the template. */
152
+ getEl<T extends Element | void = Element | void>(
153
+ key?: string,
154
+ index?: number
155
+ ): T;
156
+ /** Gets all element references by their `key` attribute in the template. */
157
+ getEls<T extends Element[] = Element[]>(key: string): T;
158
+ /** Gets a component reference by its `key` attribute in the template. */
159
+ getComponent<T extends Component | void = Component | void>(
160
+ key: string,
161
+ index?: number
162
+ ): T;
163
+ /** Gets all component references by their `key` attribute in the template. */
164
+ getComponents<T extends Component[] = Component[]>(key: string): T;
165
+ /** True if this instance has been removed from the dom. */
166
+ /** True if this instance is scheduled to rerender. */
167
+ isDestroyed(): boolean;
168
+ /** Replace the entire state object with a new one, removing old properties. */
169
+ replaceState(state: this["state"]): void;
170
+ /**
171
+ * Update a property on this.state (should prefer mutating this.state directly).
172
+ * When passed an object as the first argument, it will be merged into the state.
173
+ */
174
+ setState<Key extends PropertyKey>(
175
+ name: Key & keyof this["state"],
176
+ value: (this["state"] & Record<PropertyKey, unknown>)[Key]
177
+ ): void;
178
+ setState(value: Partial<this["state"]>): void;
179
+
180
+ /** Schedules an update related to a specific state property and optionally updates the value. */
181
+ setStateDirty<Key extends PropertyKey>(
182
+ name: Key & keyof this["state"],
183
+ value?: (this["state"] & Record<PropertyKey, unknown>)[Key]
184
+ ): void;
185
+ /** Synchronously flush any scheduled updates. */
186
+ update(): void;
187
+ /** Appends the dom for the current instance to a parent DOM element. */
188
+ appendTo(target: ParentNode): this;
189
+ /** Inserts the dom for the current instance after a sibling DOM element. */
190
+ insertAfter(target: ChildNode): this;
191
+ /** Inserts the dom for the current instance before a sibling DOM element. */
192
+ insertBefore(target: ChildNode): this;
193
+ /** Prepends the dom for the current instance to a parent DOM element. */
194
+ prependTo(target: ParentNode): this;
195
+ /** Replaces an existing DOM element with the dom for the current instance. */
196
+ replace(target: ChildNode): this;
197
+ /** Replaces the children of an existing DOM element with the dom for the current instance. */
198
+ replaceChildrenOf(target: ParentNode): this;
199
+ /** Called when the component is firsted created. */
200
+ abstract onCreate?(input: this["input"], out: Marko.Out): void;
201
+ /** Called every time the component receives input from it's parent. */
202
+ abstract onInput?(
203
+ input: this["input"],
204
+ out: Marko.Out
205
+ ): void | this["input"];
206
+ /** Called after a component has successfully rendered, but before it's update has been applied to the dom. */
207
+ abstract onRender?(out: Marko.Out): void;
208
+ /** Called after the first time the component renders and is attached to the dom. */
209
+ abstract onMount?(): void;
210
+ /** Called when a components render has been applied to the DOM (excluding when it is initially mounted). */
211
+ abstract onUpdate?(): void;
212
+ /** Called when a component is destroyed and removed from the dom. */
213
+ abstract onDestroy?(): void;
214
+ }
215
+
216
+ /** The top level api for a Marko Template. */
217
+ export abstract class Template {
218
+ /** Creates a Marko compatible output stream. */
219
+ createOut(): Out;
220
+
221
+ /**
222
+ * The folowing types are processed up by the @marko/language-tools
223
+ * and inlined into the compiled template.
224
+ *
225
+ * This is done to support generics on each of these methods
226
+ * until TypeScript supports higher kinded types.
227
+ *
228
+ * https://github.com/microsoft/TypeScript/issues/1213
229
+ */
230
+
231
+ /** @marko-overload-start */
232
+ /** Asynchronously render the template. */
233
+ abstract render(
234
+ input: Marko.TemplateInput,
235
+ stream?: {
236
+ write: (chunk: string) => void;
237
+ end: (chunk?: string) => void;
238
+ }
239
+ ): Marko.Out<Marko.Component>;
240
+
241
+ /** Synchronously render the template. */
242
+ abstract renderSync(
243
+ input: Marko.TemplateInput
244
+ ): Marko.RenderResult<Marko.Component>;
245
+
246
+ /** Synchronously render a template to a string. */
247
+ abstract renderToString(input: Marko.TemplateInput): string;
248
+
249
+ /** Render a template and return a stream.Readable in nodejs or a ReadableStream in a web worker environment. */
250
+ abstract stream(
251
+ input: Marko.TemplateInput
252
+ ): ReadableStream<string> & NodeJS.ReadableStream;
253
+ /** @marko-overload-end */
254
+ }
255
+
256
+ export interface RenderResult<
257
+ out Component extends Marko.Component = Marko.Component
258
+ > {
259
+ /** Returns the component created as a result of rendering the template. */
260
+ getComponent(): Component;
261
+ getComponents(selector?: any): any;
262
+ /** Triggers the mount lifecycle of a component without necessarily attaching it to the DOM. */
263
+ afterInsert(host?: any): this;
264
+ /** Gets the DOM node rendered by a template. */
265
+ getNode(host?: any): Node;
266
+ /** Gets the HTML output of the rendered template. */
267
+ toString(): string;
268
+ /** Appends the dom of the rendered template to a parent DOM element. */
269
+ appendTo(target: ParentNode): this;
270
+ /** Inserts the dom of the rendered template after a sibling DOM element. */
271
+ insertAfter(target: ChildNode): this;
272
+ /** Inserts the dom of the rendered template before a sibling DOM element. */
273
+ insertBefore(target: ChildNode): this;
274
+ /** Prepends the dom of the rendered template to a parent DOM element. */
275
+ prependTo(target: ParentNode): this;
276
+ /** Replaces an existing DOM element with the dom of the rendered template. */
277
+ replace(target: ChildNode): this;
278
+ /** Replaces the children of an existing DOM element with the dom of the rendered template. */
279
+ replaceChildrenOf(target: ParentNode): this;
280
+ out: Out<Component>;
281
+ /** @deprecated */
282
+ document: any;
283
+ /** @deprecated */
284
+ getOutput(): string;
285
+ /** @deprecated */
286
+ html: string;
287
+ /** @deprecated */
288
+ context: Out<Component>;
289
+ }
290
+
291
+ export interface Emitter {
292
+ listenerCount(eventName: PropertyKey): number;
293
+ emit(eventName: PropertyKey, ...args: any[]): boolean;
294
+ on(eventName: PropertyKey, listener: (...args: any[]) => any): this;
295
+ once(eventName: PropertyKey, listener: (...args: any[]) => any): this;
296
+ prependListener(
297
+ eventName: PropertyKey,
298
+ listener: (...args: any[]) => any
299
+ ): this;
300
+ removeListener(
301
+ eventName: PropertyKey,
302
+ listener: (...args: any[]) => any
303
+ ): this;
304
+ removeAllListeners(eventName?: PropertyKey): this;
305
+ }
306
+
307
+ export type Repeated<T> = [T, T, ...T[]];
308
+ export type Repeatable<T> = T | Repeated<T>;
309
+ export type MaybeRepeatable<T> = undefined | Repeatable<T>;
310
+
311
+ export interface NativeTags {
312
+ [name: string]: {
313
+ input: Record<string, unknown>;
314
+ return: unknown;
315
+ };
316
+ }
317
+ }
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "marko",
3
- "version": "5.22.2",
3
+ "version": "5.22.4",
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.23.2",
8
- "@marko/translator-default": "^5.22.2",
7
+ "@marko/compiler": "^5.23.4",
8
+ "@marko/translator-default": "^5.22.4",
9
9
  "app-module-path": "^2.2.0",
10
10
  "argly": "^1.2.0",
11
11
  "browser-refresh-client": "1.1.4",
@@ -68,8 +68,10 @@
68
68
  "components-browser.marko",
69
69
  "components.js",
70
70
  "env.js",
71
+ "index.d.ts",
71
72
  "index-browser.marko",
72
73
  "index.js",
73
74
  "node-require.js"
74
- ]
75
+ ],
76
+ "types": "index.d.ts"
75
77
  }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @template T
3
+ * @typedef {{
4
+ * value?: T;
5
+ * then?: Marko.Body<[Awaited<T>], void>;
6
+ * catch?: Marko.Body<[unknown], void>;
7
+ * placeholder?: Marko.Body<[], void>;
8
+ * client-reorder?: boolean;
9
+ * name?: string;
10
+ * timeout?: number;
11
+ * show-after?: string;
12
+ * }} Input
13
+ */