Comparison to JavaScript/TypeScript
Civet aims to be 99% compatible with existing JavaScript/TypeScript code. However, there are a few intentional deviations.
In the examples below, Civet code is on the lefttop, and compiled TypeScript output is on the rightbottom.
Single-Argument Arrow Functions
In Civet, arrow functions with a single argument need to wrap that argument in parentheses; otherwise, it gets treated like an implicit function call.
(x) => x + 1
x => x + 1
(x) => x + 1;
x(() => x + 1);
The advantage of this approach is that zero-argument functions do not need ()
to indicate arguments:
=> console.log("Hello")
createEffect => console.log(signal())
() => console.log("Hello");
createEffect(() => console.log(signal()));
Implicit Return
In Civet, all functions implicitly return the value of their last statement. You can disable this functionality by adding a semicolon at the end of the last statement, declaring a void
return type, or adding an explicit return
.
function more(x) {
x+1
}
function hello() {
console.log("Hello world!")
}
function hello() {
console.log("Hello world!");
}
function hello(): void {
console.log("Hello world!")
}
function hello() {
console.log("Hello world!")
return
}
function more(x) {
return x + 1;
}
function hello() {
return console.log("Hello world!");
}
function hello() {
console.log("Hello world!");
}
function hello(): void {
console.log("Hello world!");
}
function hello() {
console.log("Hello world!");
return;
}
By contrast, in JavaScript, only arrow functions without braces implicitly return a value. This case works in Civet too; just be sure not to have a trailing semicolon which turns off implicit returns. And in Civet you get implicit returns from arrow functions with braces or indented blocks too.
(x) => x + 1
(x) => x + 1;
=> console.log("Hello world!")
=> console.log("Hello world!");
(x) => {
if (x > 5)
"large"
else
"small"
}
(x) => x + 1;
(x) => {
x + 1;
};
() => console.log("Hello world!");
() => {
console.log("Hello world!");
};
(x) => {
if (x > 5) {
return "large";
} else {
return "small";
}
};
If you don't like implicit returns outside of single-line arrow functions, you can turn off this feature entirely via a "civet"
directive at the top of your file:
"civet -implicitReturns"
function hello() {
console.log("Hello world!")
}
=>
console.log("Hello world!")
(x) => x + 1
function hello() {
console.log("Hello world!");
}
() => {
console.log("Hello world!");
};
(x) => x + 1;
Indentation
Because Civet allows for indented blocks as shorthand for braced blocks, it generally requires you to respect your own indentation.
(x) =>
console.log("Hello")
x+1
(x) => {
console.log("Hello");
return x + 1;
};
if (condition)
console.log("condition holds")
console.log("condition still holds")
if (condition) {
console.log("condition holds");
console.log("condition still holds");
}
By contrast, in JavaScript, the last indented line would have been treated as if it were at the top level.
Automatic Semicolon Insertion
JavaScript has complicated rules for automatic semicolon insertion. Civet has a different set of rules for what separates different statements, which are hopefully closer to your intuition. Binary operators and member access can continue on the next line, but only when spaced in the natural way (e.g. space after the binary operator).
x +
y
- z
-negative
[array]
.length
{name: value}
x + y - z;
-negative;
[array].length;
({ name: value });
By contrast, JavaScript would treat the code on the lefttop as three binary operators with an index access (and a member access), followed by a code block with a label.
Labels
In Civet, labels are written :label
instead of label:
. (The one exception is $:
which can be written as $:
for Svelte compatibility.)
:label while (true) {
break label
}
$: document.title = title
label: while (true) {
break label;
}
$: document.title = title;
(This is to enable most object literals not needing braces.)
Decorators
Civet uses @
as shorthand for this
, static
, and constructor
. Decorators need to be written with @@
:
@@Object.seal
class Civet
@name = "Civet"
@Object.seal
class Civet {
static name = "Civet";
}
Civet also does not support decorators on the same line as methods. This lets you use implicit function call syntax:
class Civet
@@description translate "Caffeine time!"
drink()
@fetch @coffeeCup
class Civet {
@description(translate("Caffeine time!"))
drink() {
return this.fetch(this.coffeeCup);
}
}
JSX
To allow for automatic closing of JSX tags, Civet requires JSX children to be properly indented.
<div>
<h1>Hello</h1>
<p>Text</p>
"not inside div anymore"
<div>
<h1>Hello</h1>
<p>Text</p>
</div>;
("not inside div anymore");
If you're OK with explicitly closing all tags, you can turn off the indentation requirement via a "civet"
directive:
"civet coffeeJSX"
<div>
<h1>Hello</h1>
<p>Text</p>
still inside div
</div>
<div>
<h1>Hello</h1>
<p>Text</p>
still inside div
</div>;
Another discrepancy is that Civet automatically combines consecutive JSX tags at the same indentation level into a JSX fragment. (Otherwise, only the last tag would serve a function.)
<h1>Hello</h1>
<p>Text</p>
<>
<h1>Hello</h1>
<p>Text</p>
</>;
Anything Else
If some existing JS/TS code does not parse in Civet and it does not fall into one of the categories above, it is likely a bug; please report it.
Comparison to CoffeeScript
If you are coming to Civet from a CoffeeScript background, check out this comparison.