UNPKG

29.7 kBMarkdownView Raw
1# Helix Command Line Interface (`hlx`)
2
3## Status
4
5[![codecov](https://img.shields.io/codecov/c/github/adobe/helix-cli.svg)](https://codecov.io/gh/adobe/helix-cli)
6[![CircleCI](https://img.shields.io/circleci/project/github/adobe/helix-cli/master.svg)](https://circleci.com/gh/adobe/helix-cli/tree/master)
7[![GitHub license](https://img.shields.io/github/license/adobe/helix-cli.svg)](https://github.com/adobe/helix-cli/blob/master/LICENSE.txt)
8[![GitHub issues](https://img.shields.io/github/issues/adobe/helix-cli.svg)](https://github.com/adobe/helix-cli/issues)
9[![LGTM Code Quality Grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/adobe/helix-cli.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/adobe/helix-cli)
10
11The Helix Command Line Interface allows web developers to create, develop, and deploy digital experiences using Project Helix
12
13## Installation
14
15Install `hlx` as a global command. You need Node 10.13 or newer.
16
17```bash
18$ npm install -g @adobe/helix-cli
19```
20
21## Quick Start
22
23```
24$ hlx --help
25hlx <command>
26
27Commands:
28 hlx demo <name> [dir] Create example helix project.
29 hlx up [files...] Run a Helix development server
30 hlx build [files..] Compile the template functions and build package
31 hlx package Create Adobe I/O runtime packages
32 hlx deploy Deploy packaged functions to Adobe I/O runtime
33 hlx perf Test performance
34 hlx publish Activate strains in the Fastly CDN and publish the site
35 hlx clean Remove generated files and caches.
36 hlx completion generate bash completion script
37
38Options:
39 --version Show version number [boolean]
40 --log-file Log file (use "-" for stdout) [array] [default: "-"]
41 --log-level Log level
42 [string] [choices: "silly", "debug", "verbose", "info", "warn", "error"]
43 [default: "info"]
44 --help Show help [boolean]
45
46for more information, find our manual at https://github.com/adobe/helix-cli
47```
48
49## Setting up a project
50
51```
52$ hlx demo <my-cool-project>
53```
54
55## Starting development
56
57```
58$ cd <my-cool-project>
59$ hlx up
60```
61
62Just change contents in your project directory and reload `http://localhost:3000` to see the results.
63
64## (Optional) Build artifacts
65
66```
67# In <my-cool-project>
68$ hlx build
69```
70
71## (Optional) Deploy to Adobe I/O Runtime
72
73### Automatic Deployment
74
75By default, Helix will set up automated deployment that deploys whenever a new commit has been pushed to your GitHub code repository. In order to do so, you need a [CircleCI](https://circleci.com) account and generate a [personal API Token](https://circleci.com/account/api).
76
77```
78# In <my-cool-project>
79$ hlx deploy \
80 --circleci-auth <personal-api-token> \
81 --wsk-namespace <your-namespace> \
82 --wsk-auth <your-key> \
83 --fastly-auth <key> \
84 --fastly-namespace <serviceid>
85```
86
87As always, you can keep all parameters in `HLX_CIRCLECI_AUTH`, `HLX_WSK_AUTH`, `HLX_DEV_DEFAULT` and `HLX_FASTLY_AUTH` environment variables if you don't want them in your `.bash_history`.
88
89### One-Shot Deployment
90
91Alternatively, you can also perform a one-shot deployment like this:
92
93```
94# In <my-cool-project>
95$ hlx deploy --wsk-namespace <your-namespace> --wsk-auth <your-key>
96[==================================================] analyzing 0.0s
97[==================================================] packaging 0.0s
98✅ packaging completed
99[==================================================] deploying 0.0s
100✅ deployment completed
101```
102
103Instead of passing `--wsk-auth` as a command line option, you can also set the `HLX_WSK_AUTH` environment variable.
104
105## (Optional) Publish your Site
106
107```
108# In <my-cool-project>
109$ hlx publish --fastly-auth <key> --fastly-namespace <serviceid>
110Publishing [========================================----------] 4.1s
111✅ All strains have been published and version 89 is now online.
112```
113
114### Purging the Cache upon Publishing
115
116Whenever you run `hlx publish`, a new version of your site, with potentially changed code will be made available to visitors. For visitors to see the changes, the Fastly cache needs to be purged. By default, `hlx publish` uses a "Soft purge", which means that the entire content of your website will be marked as outdated (or stale), but not actually removed from the cache. When a request for a cached file that has been marked outdated hits Fastly, Fastly will serve the old version, but fetch a new version in the background. As a result, your site is still as fast as before, *but in order for changes to show up, two requests are needed*.
117
118If you want to see your changes faster, at the expense of slower load times right after publishing, use the command `hlx publish --purge hard`, which triggers a hard purge, i.e. removes all cached objects from the Fastly CDN. Doing this on a site with substantial traffic is unwise, but it can be a useful option during development.
119
120Finally, if you do not want the cache to be purged at all, run `hlx publish --purge skip`. Your changes will only become visible when the cached objects expire or the cache is cleared in some other way, for instance from the Fastly console or using an API call as part of a more complex continuous deployment set-up.
121
122### Passing Request Parameters
123
124Every request parameter is a potential cache-buster and given that modern web application practices liberally append request parameters for tracking purposes or to manage state for client-side applications, **Helix filters out all request parameters by default**.
125
126This means, the client side of your application will still be able to access request parameters, but your server(less)-side scripts and templates will not see any parameters.
127
128If you need to pass request parameters, you can allow the parameters you need using the `strain.params` configuration. The value of `params` is an array of allowed parameter names.
129
130```yaml
131strains:
132 - name: default
133 code: https://github.com/adobe/project-helix.io.git#master
134 content: https://github.com/adobe/project-helix.io.git#master
135 static: https://github.com/adobe/project-helix.io.git/htdocs#master
136 params:
137 - foo
138 - bar
139```
140
141In the example above, the parameters `foo` and `bar` have been enabled. A request made to `https://www.example.com/index.html?foo=here&bar=there&baz=everywhere` will enable your application to read the `foo` and `bar` parameters. The `baz` parameter and all other parameters will be filtered out.
142
143Every allowed parameter value will affect the caching of your site in the CDN.
144
145#### Helix-Internal Request Parameters
146
147All request parameters starting with `hlx_` will be passed through to the action, so that they can be used for Helix-internal purposes.
148
149### Directory Index
150
151The default behavior for directory indexes is to load `index.html` when requesting a path ending with `/`,
152so that `/foo/bar/` becomes `/foo/bar/index.html`. This setting can be overwritten in `helix-config.yaml`
153by adding an `index` property:
154
155```yaml
156strains:
157 - name: default
158 code: https://github.com/adobe/project-helix.io.git#master
159 content: https://github.com/adobe/project-helix.io.git#master
160 static: https://github.com/adobe/project-helix.io.git/htdocs#master
161 directoryIndex: README.html
162```
163
164### Static Content Handling
165
166Static content is delivered from the `htdocs` directory of the _code_ repository of the Helix project:
167
168```yaml
169strains:
170 - name: default
171 code: https://github.com/adobe/project-helix.io.git#master
172 content: https://github.com/adobe/project-helix.io.git#master
173 static: https://github.com/adobe/project-helix.io.git/htdocs#master
174```
175
176The same core configuration options (`repo`, `ref`, `root`, and `owner`) are supported for `static` as for `content`.
177
178After your next deployment with `hlx publish`, all static content will be served out of the
179directory `htdocs`. None of this will be visible in the URL, so that no visitor will ever see
180_htdocs_ in the URL. `https://example.com/favico.ico` would be served from `$REPO/htdocs/favico.ico`.
181
182## Matching Strains to URLs
183
184You can define a `url` for each `strain`. This property will make sure that only requests made
185to this base URL will be mapped to the following URL, enabling patterns like having a production
186instance on `www.*` and a development instance on `dev.*`.
187
188An example configuration could look like this:
189
190```yaml
191strains:
192 - name: default
193 code: https://github.com/adobe/project-helix.io.git#master
194 content: https://github.com/adobe/project-helix.io.git#master
195 static: https://github.com/adobe/project-helix.io.git/htdocs#master
196 condition:
197 url: https://www.primordialsoup.life
198
199 - name: develop
200 code: https://github.com/adobe/project-helix.io.git#dev
201 content: https://github.com/adobe/project-helix.io.git#master
202 static: https://github.com/adobe/project-helix.io.git/htdocs#master
203 condition:
204 url: https://dev.primordialsoup.life/develop/
205```
206
207## Mixing old and new Content
208
209Helix can run old and new versions of the same site side by side, and even intermixed. This allows you to gradually upgrade to using Helix.
210
211If you want to serve content from another origin server, just add the property `origin` to any strain. `code`, `content`, `directoryIndex`, and most other properties will then be ignored, as all content for that strain will be retrieved from the URL specified in `origin`.
212
213You are still able to set strain `conditions` or assign traffic to a strain based on the `url` property.
214
215```yaml
216strains:
217 - name: default
218 code: https://github.com/adobe/project-helix.io.git#master
219 content: https://github.com/adobe/project-helix.io.git#master
220 static: https://github.com/adobe/project-helix.io.git/htdocs#master
221
222 - name: oldcontent
223 origin: https://www.adobe.io
224 condition:
225 url: https://www.primordialsoup.life/content/
226
227 - name: proxy
228 origin: https://www.adobe.io
229 condition: req.http.host == "proxy.primordialsoup.life"
230```
231
232In the example above, there are three strains: `default` serves content from `www.primordialsoup.life` using Helix. But all URLs that start with `https://www.primordialsoup.life/content/` will be served from `www.adobe.io`. This means an image that is referenced as `/content/example.png` will be served from the Adobe I/O website.
233
234Finally, on `proxy.primordialsoup.life`, all content of the old site is being served. This allows you to easily switch back to an old configuration.
235
236## Development - Serving local content
237
238Getting the Helix Development Server to use a local content repository can be done in 2 ways:
239
240### Specify a local content url
241
242This is the out-of-the box setup:
243
244```yaml
245definitions:
246 defaults:
247 - &localRepo "http://localhost/local/default.git"
248
249strains:
250 - name: default
251 condition:
252 url: http://localhost:3000/
253 code: *localRepo
254 content: *localRepo
255 static: *localRepo
256```
257
258The Helix Development server will automatically start a git server that can serve the content from
259the local repository.
260
261### Use the GitHub emulator
262
263When starting the `hlx up` with `--local-repo` argument(s), it instructs the Helix Development Server to
264start a git server that emulates GitHub repositories for a local git repository. All the strains that
265have a _content_ or _static_ url that matches the `origin` of emulated repository are internally reconfigured
266to use the local git server instead.
267
268`--local-repo .` is the implicit default. For the simple case, where only one repository is used for code, content and static just do:
269
270```
271$ hlx up
272```
273
274which is equivalent to `hlx up --local-repo .`.
275
276If you want to explicitly always fetch from GitHub, i.e. ignore the local checkout in the current working directory (or any other checkout specified with `--local-repo`), use `--no-local-repo`:
277
278```
279$ hlx up --no-local-repo
280```
281
282## Passing default action parameters during deploy
283
284If the action needs default parameter, they can be specified during `hlx deploy` with the `--default` argument. The argument either takes multiple name/value pairs or json values. They can also be read from environment or json files via the `--default-file` argument.
285
286### use as argument values
287
288```console
289# arguments with name value pairs
290hlx deploy --default SECRET value --default ANOTHER foobar
291
292# multiple name value pairs
293hlx deploy --default SECRET value ANOTHER foobar
294
295# json argument
296hlx deploy --default '{"SECRET": "value", "ANOTHER": "foobar"}'
297```
298
299#### use in environment
300
301All `hlx` command line arguments can also be passed via environment variables, prefixed with `HLX_`. Since the `default` parameters need key and value, this is only possible using the json format:
302
303(note that the `.env` file is automatically loaded by `hlx`)
304
305**.env**
306```env
307HLX_DEFAULT={"SECRET": "value", "ANOTHER": "foobar"}
308```
309
310### use via reference to file
311
312In addition to the above, the parameters can also be specified using a reference to a json or env file:
313
314```console
315# reference to env file
316hlx deploy --default-file secrets.env
317
318# reference to json file
319hlx deploy --default-file ./prod/secrets.json
320```
321
322**secrets.env**
323```env
324SECRET=value
325ANOTHER=foobar
326```
327
328**prod/secrets.json**
329```json
330{
331 "SECRET": "value",
332 "ANOTHER": "foobar"
333}
334```
335
336#### use in environment
337
338similar to the above, the file reference can also be specified in the environment variables:
339
340**.env**
341```
342HLX_DEFAULT_FILE=./prod/secrets.json
343```
344
345### Passing default action parameters to the simulator
346
347When testing helix locally with `hlx up` the `--dev-default` can be used to specify the action
348parameters which the simulator should pass to the action.
349
350the semantics of the arguments and environment variables is the same as for the `--default` and `--default-file` arguments above.
351
352For example, to configure request timeouts:
353
354```
355$ hlx up --dev-default HTTP_TIMEOUT 2000
356```
357
358Developers can pass additional action parameters by setting the
359`HLX_DEV_DEFAULT` environment variable. This must fulfill JSON string formatting;
360i.e `$HLX_DEV_DEFAULT='{"KEY1":5000, "KEY2":"VALUE2"}'`
361
362For a list of known parameters, see [the Helix Pipeline Configuration Parameters documentation](https://github.com/adobe/helix-pipeline/blob/master/docs/secrets.schema.md#secrets-properties)
363
364
365#### Multi Strain Example
366
367In the following config, we define 2 repositories:
368
369- `defaultRepo` contains the project's code and the main content
370- `apiRepo` contains additional content; for example the API documentation.
371
372We also define 2 strains, one for each purpose.
373
374```yaml
375definitions:
376 repos:
377 - &defaultRepo https://github.com/helix/welcome.git#master
378 - &apiRepo https://github.com/helix/welcome-api.git#master
379
380strains:
381 - name: api
382 condition:
383 url: https://www.project-helix.io/api
384 code: *defaultRepo
385 content: *apiRepo
386 static: *apiRepo
387
388 - name: default
389 condition:
390 url: https://www.project-helix.io/
391 code: *defaultRepo
392 content: *defaultRepo
393 static: *defaultRepo
394```
395
396Usually, when invoking `hlx up` without any arguments, the Helix Development Server will serve the
397content directly from GitHub. This is not suitable for local development. Also, the `api` strain
398will never be selected, because the `localhost:3000` host header will not match the specified `url`
399condition.
400
401Starting the server with:
402
403```
404$ hlx up --host=www.project-helix.io
405```
406
407Solves the latter problem. the `--host` argument internally overrides the `request.header`, so that
408the strain resolution works as desired.
409
410Assume that we also have a local checkout of the `welcome-api`, beside the `welcome` repository:
411
412```
413projects/
414├── welcome/
415│   ├── helix-config.yaml
416│   └── index.md
417└── welcome-api/
418 └── index.md
419```
420
421We can now launch the server with the respective `--local-repo` arguments:
422
423```
424$ hlx up --host=www.project-helix.io --local-repo=. --local-repo=../welcome-api
425```
426
427Now the server will transiently reconfigure the strains, so that the emulated repositories are used.
428
429> **Note**: If you turn on `--log-level=debug` you should see log entries for the emulated repositories:
430```
431[hlx] debug: git emulating https://github.com/helix/welcome.git via http://127.0.0.1:52270/helix/github.com--helix--welcome.git#master from './'
432[hlx] debug: git emulating https://github.com/helix/welcome-api.git via http://127.0.0.1:52270/helix/github.com--helix-welcome-api.git#master from '../welcome-api'
433```
434
435For convenience, you can also specify the arguments in an `.env` file:
436
437```dotenv
438HLX_HOST=www.project-helix.io
439HLX_LOCAL_REPO=., ../welcome-api
440HLX_LOG_LEVEL=debug
441```
442
443
444## (Recommended) Performance Testing
445
446You can (and should) test the performance of your deployed site by running `hlx perf`.
447
448The default test will test the entry page of every strain (using the `url`) property, if defined. Additional known URLs can be configured for each strain using the key `urls` (expects an array of URLs).
449
450The default test will run from a mid-range mobile phone (Motorola Moto G4), using a regular 3G connection from London, UK. It makes sure that the Lighthouse Accessibility Score and the Lighthouse Performance Score of your site is at least 80.
451
452You can set custom performance budgets and change the performance condition for each strain using the `perf` property. If a strain has no `perf` measurement configured, the `perf` configuration of the default strain will be used.
453
454An example performance configuration might look like this:
455
456```yaml
457strains:
458 - name: default
459 code: https://github.com/adobe/project-helix.io.git#master
460 content: https://github.com/adobe/project-helix.io.git#master
461 static: https://github.com/adobe/project-helix.io.git/htdocs#master
462 condition:
463 url: https://www.primordialsoup.life
464 urls:
465 - https://www.primordialsoup.life/README.html
466 perf:
467 device: iPhone8
468 connection: good3G
469 location: Sydney
470 visually_complete_85: 1500
471 lighthouse-best-practices-score: 80
472```
473
474If the site does not meet all performance criteria you have defined, `hlx perf` will exit with a non-null exit code (the exit code equals the number of failed tests). This allows you to use `hlx perf` as a gating condition in a CI/CD workflow.
475
476### Testing Environment
477
478* Possible `device` values are:
479 * `MotorolaMotoG4`
480 * `iPhone5`
481 * `iPhone6`
482 * `iPhone6Plus`
483 * `iPhone7`
484 * `iPhone8`
485 * `Nexus5X`
486 * `Nexus6P`
487 * `GalaxyS5`
488 * `iPad`
489 * `iPadPro`
490* Possible `connection` values are:
491 * `regular2G`
492 * `good2G`
493 * `slow3G`
494 * `regular3G`
495 * `good3G`
496 * `emergingMarkets`
497 * `regular4G`
498 * `LTE`
499 * `dsl`
500 * `wifi`
501 * `cable`
502* Possible `location` values are:
503 * `NorthVirginia`
504 * `Frankfurt`
505 * `Sydney`
506 * `Ohio`
507 * `California`
508 * `Oregon`
509 * `Canada`
510 * `Ireland`
511 * `Tokyo`
512 * `Seoul`
513 * `Singapore`
514 * `Mumbai`
515 * `SaoPaulo`
516 * `London`
517
518### Performance Metrics
519
520You can set performance budgets against following scores (more is better) and metrics (less is better):
521
522* `speed_index`: Speed Index
523* `visually_complete`: Visually Complete
524* `visually_complete_85`: 85% Visually Complete
525* `lighthouse-seo-score`: Lighthouse SEO Score
526* `lighthouse-best-practices-score`: Lighthouse Best Practices Score
527* `lighthouse-accessibility-score`: Lighthouse Accessibility Score
528* `lighthouse-performance-score`: Lighthouse Performance Score
529* `lighthouse-pwa-score`: Lighthouse Progressive Web App Score
530* `js-parse-compile`: JS Parse & Compile
531* `time-to-first-byte`: Time to First Byte
532* `first-contentful-paint`: First Contentful Paint
533* `first-meaningful-paint`: First Meaningful Paint
534* `firstRender`: First Paint
535* `dom-size`: DOM Element Count
536* `estimated-input-latency`: Estimated input latency
537* `consistently-interactive`: Time to Interactive
538* `first-interactive`: First CPU Idle
539* `html_body_size_in_bytes`: Total HTML size in bytes
540* `html_size_in_bytes`: Total HTML transferred
541* `page_wait_timing`: Response time
542* `page_size_in_bytes`: Total Page transferred
543* `page_body_size_in_bytes`: Total Page size in bytes
544* `asset_count`: Number of requests
545* `onload`: onLoad
546* `oncontentload`: onContentLoad
547
548#### Structured (JUnit) Performance Reporting
549
550By calling `hlx perf` with the option `--junit <file>`, the performance test
551results will be reported in JUnit-format, which makes it possible to integrate
552performance result reporting with the CI system performing an automated deployment.
553
554For `hlx demo full`, a full CI configuration is created that will run a performance
555test after a completed deployment, report the per-metric results and mark the build
556as failed in case metrics are not met.
557
558## Supported Programming Languages
559
560Helix allows you to develop experiences using a number of languages in different contexts. The most important languages are:
561
562* HTL
563* JavaScript
564* JSX
565
566Please note that these languages are all executed server-side (or serverless-side, as the code is on Adobe I/O Runtime). In some cases this means that you can move code between client and server with moderate changes.
567
568### Creating Things in Helix with HTL
569
570HTL stands for [HTML Template Language and was originally introduced for Adobe Experience Manager](https://docs.adobe.com/content/help/en/experience-manager-htl/using/getting-started/update.html). The implementation in Helix is based on the [HTL Specification](https://github.com/adobe/htl-spec/blob/master/SPECIFICATION.md), but as Helix and the underlying [`htlengine`](https://github.com/adobe/htlengine) are written in JavaScript rather than Java, and as the object model between Helix and AEM is different (check out the [`helix-pipeline` documentation](https://github.com/adobe/helix-pipeline/tree/master/docs) for Helix' domain model), your templates translate roughly rather than directly.
571
572You can use HTL within Helix in exactly one context: to create rendering templates for pages or page fragments. Your HTL templates will be compiled by Helix into a JavaScript function, which you can then invoke on Adobe I/O Runtime (through Fastly) or locally (through the Helix Simulator). Rendering templates operate on the current [`context`](https://github.com/adobe/helix-pipeline/blob/master/docs/context.schema.md) and return a HTML string that will be delivered to the browser.
573
574HTL templates follow the naming pattern `src/${extension}.htl` or `src/${selector}_${extension}.htl`, for instance `src/html.htl` or `src/footer_html.htl`.
575
576Because HTL is a pure declarative templating language, you cannot make any modifications within HTL to change the context. To do that, you need to use JavaScript, which is explained in the next section.
577
578### Creating Things in Helix with JavaScript
579
580JavaScript is the universal language that powers Helix and you can use it in a wide array of settings in Helix:
581
5821. to create HTML, JSON, Text, XML, or other documents to be served to the browser (as a template function)
5832. to modify and manipulate the [`context`](https://github.com/adobe/helix-pipeline/blob/master/docs/context.schema.md) before it is handed off to a template function (as `pre.js`)
5843. to handle requests for forms, web applications, and to create small APIs (as `cgi-bin`)
5854. to provide helper functions that can be used elsewhere in Helix (as modules)
586
587#### JavaScript Template Functions
588
589A JavaScript template functions is a step in the Helix rendering Pipeline that takes the current [`context`](https://github.com/adobe/helix-pipeline/blob/master/docs/context.schema.md) and sets the `context`'s [`response.body`](https://github.com/adobe/helix-pipeline/blob/master/docs/response.schema.md#body). It is a full-powered (serverless) JavaScript function, so you can do whatever you want, include any NPM module that's useful, as long as the function is fast enough to be executed within a couple of seconds.
590
591JavaScript template functions are found in files that are follow the naming pattern `src/${extension}.js` or `src/${selector}_${extension}.js`, for instance `src/html.js` or `src/footer_html.js`. Only a number of `extension`s are allowed, including `html`, `json`, `txt`, `xml`, `svg`, and `css`.
592
593A minimal functional JavaScript template function must export a `main` function and should set the `context`'s [`response.body`](https://github.com/adobe/helix-pipeline/blob/master/docs/response.schema.md#body) property.
594
595```javascript
596// exporting `main` is mandatory
597module.exports.main = (context, action) {
598 context.response = {
599 // setting the body is the purpose of the function
600 body: 'Hello World'
601 };
602}
603```
604
605#### JavaScript `pre.js`
606
607A JavaScript `pre.js` ("pree-jay-ess") is a collection of JavaScript functions that will be executed by the Helix Pipeline right before the template function gets called. This allows a `pre.js` to prepare the context in a way that makes it easier to use in a template function.
608
609In addition, a `pre.js` can use [additional extension points in the pipeline](https://github.com/adobe/helix-pipeline#extension-points), but the step running right before the template function is the most common extension point that gave the `pre.js` its name.
610
611`pre.js` files follow the naming pattern `src/${extension}.pre.js` or `src/${selector}_${extension}.pre.js`, for instance `src/html.pre.js` or `src/footer_html.pre.js`. They are the companions of the template functions (in HTL, JavaScript or JSX with the same selector and extension).
612
613A minimal `pre.js` must exports a `pre` function and has access to the [`context`](https://github.com/adobe/helix-pipeline/blob/master/docs/context.schema.md) and [`action`](https://github.com/adobe/helix-pipeline/blob/master/docs/action.schema.md) of the pipeline.
614
615```javascript
616module.exports.pre = (context, action) => {
617 console.log('I am here. You can see this log message in the Adobe I/O Runtime console.');
618}
619```
620
621#### JavaScript `cgi-bin`
622
623Template functions and `pre.js` have in common that they have no side effects, i.e. they cannot do anything other than change the `context` and render web experiences. This reflects the fact that they get used only to serve `GET` requests and are heavily cached, so that most visitors coming to your site won't actually run code in the Adobe I/O Runtime (which keeps your costs low), but this also means that you should not rely on them when you want actual work done, databases to be written, or emails to be sent.
624
625For this, Helix provides you with a simple way of creating, deploying, and running serverless actions that **can** have side-effects. In [the spirit of 1997](https://medium.com/adobetech/2017-will-be-the-year-of-the-cgi-bin-err-serverless-f5d99671bc99), we call it the `cgi-bin`, and it is a place for scripts that are running on your behalf in Adobe I/O Runtime. They get deployed using `hlx deploy` with all your other code, they support multiple parallel deployments, CD, and all the best practices of 2019, but at the ease of development of 1997.
626
627In order to create a `cgi-bin` script, all you need to do is to create a `.js` file in the `cgi-bin` directory, such as `hello.js`.
628
629```javascript
630module.exports.main = (params) => {
631 var name = params.name || 'World';
632 return {payload: 'Hello, ' + name + '!'};
633}
634```
635
636This is the ["Hello World" example from Apache OpenWhisk](https://github.com/apache/incubator-openwhisk/blob/master/docs/samples.md#openwhisk-hello-world-example) and it can be used to create a very simple JSON API, which supports POST requests (with a multipart-formdata or JSON body) and GET requests (with URL parameters).
637
638#### JavaScript Modules
639
640If there are additional helper functions you need in multiple parts of your project, you can simply put them into a JavaScript module below `src`. Make sure to `export` the functions and objects you want to consume in your `cgi-bin`, `pre.js`, or template functions.
641
642### Creating Things in Helix with JSX
643
644[JSX is an extension of the ES6 language](https://facebook.github.io/jsx/), originally created by Facebook for the client-side React framework, but, due to its practicality, adopted by [other frameworks](https://mithril.js.org/jsx.html) and is even used [on the server-side](https://nextjs.org).
645
646JSX provides a shorthand syntax for creating DOM elements, which makes it well suited for creating templates using multiple components (really just functions) that are re-usable and re-mixable.
647
648In Helix, JSX is used for serverless-side rendering of HTML pages or HTML page fragments, making it a language choice for Template Functions and an alternative for [JavaScript Template Functions](#javascript-template-functions).
649
650The ability to mix imperative JavaScript code with HTML-generating functions that look almost like real HTML makes JSX an alternative to using HTL with `pre.js`, too, because you can just keep the pre-processing code inside your JSX file.
651
652JSX files im Helix follow the naming pattern `src/${extension}.jsx` or `src/${selector}_${extension}.jsx`, for instance `src/html.jsx` or `src/footer_html.jsx`.
653
654Like [JavaScript Template Functions](#javascript-template-functions), JSX operates on the `context`, produces `context.response.body` and needs a `main` entry point. A minimal JSX example would look like this:
655
656```jsx
657function MyComponent(context) {
658 <div>Hello World</div>
659}
660
661module.exports.main = context => {
662 context.response: {
663 // the response body needs to be a string, so taking the `outerHTML` of
664 // the topmost component is a good choice.
665 body: MyComponent(context).outerHTML
666 };
667};
668```
669
670# Developing Helix CLI
671
672## Testing
673
674You can use `npm run check` to run the tests and check whether your code adheres
675to the helix-cli coding style.