Skip to content

Commit d02f6c0

Browse files
committed
Fixes #77 Fixes #75 (docs for webc:if webc:type=js)
1 parent b5c4d4f commit d02f6c0

6 files changed

+318
-17
lines changed

.editorconfig

+4
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@ indent_style = tab
55
indent_size = 2
66
end_of_line = lf
77
charset = utf-8
8+
9+
[/test/**/*]
10+
trim_trailing_whitespace = false
11+
insert_final_newline = unset

README.md

+75-9
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,20 @@ Note that the `<template webc:type>` node is compiled away. If you’d like to k
412412

413413
We do provide two built-in transforms in WebC: JavaScript Render Functions (`webc:type="render"`) and CSS scoping (`webc:scoped`). Those are covered in separate sections. You _can_ override these with the `setTransform` API but it is generally recommended to add your own named transform!
414414

415+
### Conditionals
416+
417+
_(WebC v0.7.1+)_
418+
419+
Use `webc:if` to conditionally render elements. Accepts arbitrary JavaScript (and is async-friendly). Similar to dynamic attributes, this also has access to component attributes and properties.
420+
421+
```html
422+
<div webc:if="true">This will render</div>
423+
<div webc:if="false">This will not render</div>
424+
<div webc:if="myAsyncHelper()">If the helper promise resolves to a truthy value, this will render</div>
425+
```
426+
427+
For more complex conditionals, `webc:type="js"` _(WebC v0.7.1+)_ is recommended (read more below).
428+
415429
### Attributes
416430

417431
Consider this example:
@@ -473,7 +487,16 @@ Make any attribute into a prop by prefixing it with `@`. Props are “private”
473487

474488
### JavaScript Render Functions
475489

476-
You can also transform individual element content using `webc:type`. We provide one built-in type, `render` for JavaScript render functions. These are async friendly (e.g. `async function()`):
490+
You can also transform individual element content using `webc:type`. There are three built-in types:
491+
492+
* `webc:type="js"` which supercedes `webc:type="render"`
493+
* `webc:type="css:scoped"` (internal for `webc:scoped`—overridable!)
494+
495+
JavaScript Render Functions are async friendly (e.g. `async function()`):
496+
497+
#### `webc:type="js"` _(WebC v0.7.1+)_
498+
499+
Run any arbitrary server JavaScript in WebC. Outputs the result of the very last statement executed in the script. Async-friendly (return a promise and we’ll resolve it).
477500

478501
`page.webc`:
479502

@@ -483,18 +506,32 @@ You can also transform individual element content using `webc:type`. We provide
483506

484507
`components/img.webc`:
485508

509+
```html
510+
<script webc:type="js" webc:is="template">
511+
if(!alt) {
512+
throw new Error("oh no you didn’t");
513+
}
514+
`<img src="${src}" alt="${alt}">`;
515+
</script>
516+
```
517+
518+
<details>
519+
<summary>Expand to see this example with <code>webc:type="render"</code></summary>
520+
486521
```html
487522
<script webc:type="render" webc:is="template">
488-
function() {
489-
if(!this.alt) {
490-
throw new Error("oh no you didn’t");
523+
function() {
524+
if(!this.alt) {
525+
throw new Error("oh no you didn’t");
491526
}
492527
// Free idea: use the Eleventy Image plugin to return optimized markup
493528
return `<img src="${this.src}" alt="${this.alt}">`;
494529
}
495530
</script>
496531
```
497532

533+
</details>
534+
498535
Or use a JavaScript render function to generate some CSS:
499536

500537
`page.webc`:
@@ -507,6 +544,14 @@ Or use a JavaScript render function to generate some CSS:
507544

508545
`components/add-banner-to-css.webc`:
509546

547+
```html
548+
<script webc:type="js" webc:is="style">`/* ${license} */`</script>
549+
<slot></slot>
550+
```
551+
552+
<details>
553+
<summary>Expand to see this example with <code>webc:type="render"</code></summary>
554+
510555
```html
511556
<script webc:type="render" webc:is="style">
512557
function() {
@@ -516,11 +561,32 @@ function() {
516561
<slot></slot>
517562
```
518563

519-
(Yes you can use `<script webc:type="render" webc:scoped>` here too).
564+
</details>
565+
566+
(Yes you can use `<script webc:type="js" webc:scoped>` too).
567+
568+
A few more examples of conditionals:
569+
570+
```html
571+
<script webc:type="js">
572+
alt ? `<img src="${src}" alt="${alt}">` : `<a href="${src}">Your image didn’t have an alt so you get this link instead.</a>`
573+
</script>
574+
```
520575

521576
Note that you have access to the component attributes and properties in the render function (which is covered in another section!).
522577

523-
#### Setting HTML
578+
```html
579+
<script webc:type="js">
580+
if(alt) {
581+
`<img src="${src}" alt="${alt}">`
582+
} else {
583+
`<a href="${src}">Your image didn’t have an alt so you get this link instead.</a>`
584+
}
585+
</script>
586+
```
587+
588+
589+
### Setting HTML
524590

525591
We provide a special `@html` property to override any tag content with custom JavaScript.
526592

@@ -537,7 +603,7 @@ We provide a special `@html` property to override any tag content with custom Ja
537603
* Using `webc:raw` will prevent reprocessing the result as WebC. (v0.6.0+)
538604
* Use `@raw` as an alias for `webc:raw @html` . (v0.7.1+)
539605

540-
#### Setting Text
606+
### Setting Text
541607

542608
We provide a special `@text` property to override any tag content with custom JavaScript. The entire value returned here will be escaped!
543609

@@ -553,7 +619,7 @@ We provide a special `@text` property to override any tag content with custom Ja
553619
<p @text="dataProperty" webc:nokeep></p>
554620
```
555621

556-
#### Helper Functions
622+
### Helper Functions
557623

558624
If you want to add custom JavaScript functions for use in render functions, `@html`, or dynamic attributes you can use the `setHelper` method.
559625

@@ -579,7 +645,7 @@ function() {
579645

580646
### Raw Content (no WebC processing)
581647

582-
Opt out of WebC template processing using `webc:raw`. This works well with `<template>` content.
648+
Opt out of WebC template processing using `webc:raw`. This works well with `<template>` content. See also the special `@raw` content property _(WebC v0.7.1+)_
583649

584650
```html
585651
<template webc:raw>

src/ast.js

+4-5
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ class AstSerializer {
127127
this.setTransform(AstSerializer.transformTypes.JS, function(content) {
128128
// returns promise
129129
return ModuleScript.evaluateScript(`${AstSerializer.attrs.RENDER}="${AstSerializer.transformTypes.JS}"`, content, this, {
130-
allowRequire: true
130+
injectGlobals: true
131131
});
132132
});
133133

@@ -181,7 +181,6 @@ class AstSerializer {
181181
static transformTypes = {
182182
JS: "js",
183183
RENDER: "render",
184-
MODULE: "module", // alias for render
185184
SCOPED: "css:scoped",
186185
};
187186

@@ -1250,9 +1249,9 @@ class AstSerializer {
12501249

12511250
let ifExprContent = this.getAttributeValue(node, AstSerializer.attrs.IF);
12521251
if(ifExprContent) {
1253-
let ifExprValue = await this.evaluateAttribute(AstSerializer.attrs.HTML, ifExprContent, options);
1254-
if(ifExprValue === false) {
1255-
return false;
1252+
let ifExprValue = await this.evaluateAttribute(AstSerializer.attrs.IF, ifExprContent, options);
1253+
if(!ifExprValue) {
1254+
return { html: "" };
12561255
}
12571256
}
12581257

src/moduleScript.cjs

+11-2
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,19 @@ class ModuleScript {
2323

2424
static async evaluateScript(name, content, data, options = {}) {
2525
options = Object.assign({
26-
allowRequire: false
26+
injectGlobals: false
2727
}, options);
2828

2929
try {
3030
let context = ModuleScript.getProxiedContext(data);
31-
if(options.allowRequire) {
31+
32+
if(options.injectGlobals) {
33+
// Add globals https://nodejs.org/api/globals.html#global
34+
context = {
35+
...global,
36+
...context,
37+
};
38+
3239
context.require = function(target) {
3340
const path = require("path");
3441

@@ -40,6 +47,8 @@ class ModuleScript {
4047

4148
return require(target)
4249
};
50+
51+
// TODO wrap in function if the `js` content includes a `return` statement
4352
}
4453

4554
let returnValue = vm.runInNewContext(content, context, {

test/flowcontrolTest.js

+85
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,29 @@
11
import test from "ava";
22
import { WebC } from "../webc.js";
33

4+
test("Using webc:if=undefined", async t => {
5+
let component = new WebC();
6+
7+
component.setContent(`<!doctype html>
8+
<html>
9+
<body><div webc:if="myText">Hi</div></body>
10+
</html>`);
11+
12+
let { html, css, js, components } = await component.compile({
13+
data: {}
14+
});
15+
16+
t.deepEqual(js, []);
17+
t.deepEqual(css, []);
18+
t.deepEqual(components, []);
19+
20+
t.is(html, `<!doctype html>
21+
<html>
22+
<head></head><body>
23+
</body>
24+
</html>`);
25+
});
26+
427
test("Using webc:if=false", async t => {
528
let component = new WebC();
629

@@ -50,3 +73,65 @@ test("Using webc:if=true", async t => {
5073
</body>
5174
</html>`);
5275
});
76+
77+
test("Using webc:if with a promise (resolves true)", async t => {
78+
let component = new WebC();
79+
component.setHelper("timeoutTrue", async () => {
80+
return new Promise(resolve => {
81+
setTimeout(() => {
82+
resolve(true);
83+
}, 50);
84+
});
85+
})
86+
component.setContent(`<!doctype html>
87+
<html>
88+
<body><div webc:if="timeoutTrue()">Hi</div></body>
89+
</html>`);
90+
91+
let { html, css, js, components } = await component.compile({
92+
data: {
93+
myText: true
94+
}
95+
});
96+
97+
t.deepEqual(js, []);
98+
t.deepEqual(css, []);
99+
t.deepEqual(components, []);
100+
101+
t.is(html, `<!doctype html>
102+
<html>
103+
<head></head><body><div>Hi</div>
104+
</body>
105+
</html>`);
106+
});
107+
108+
test("Using webc:if with a promise (resolves false)", async t => {
109+
let component = new WebC();
110+
component.setHelper("timeoutTrue", async () => {
111+
return new Promise(resolve => {
112+
setTimeout(() => {
113+
resolve(false);
114+
}, 50);
115+
});
116+
})
117+
component.setContent(`<!doctype html>
118+
<html>
119+
<body><div webc:if="timeoutTrue()">Hi</div></body>
120+
</html>`);
121+
122+
let { html, css, js, components } = await component.compile({
123+
data: {
124+
myText: true
125+
}
126+
});
127+
128+
t.deepEqual(js, []);
129+
t.deepEqual(css, []);
130+
t.deepEqual(components, []);
131+
132+
t.is(html, `<!doctype html>
133+
<html>
134+
<head></head><body>
135+
</body>
136+
</html>`);
137+
});

0 commit comments

Comments
 (0)