A TypeScript superset that favors more types and less typing
MIT License
The modern way to write TypeScript.
<script>
tag
# Install
npm install -g @danielx/civet
# Run Civet code directly in a REPL
civet
# Transpile typed Civet code into TypeScript in a REPL
civet -c
# Compile Civet source file to TypeScript
civet < source.civet > output.ts
# Execute a .civet script
civet source.civet ...args...
# Execute a .civet source file in node
node --import @danielx/civet/register source.civet
ts, {CompilerOptions} from typescript
DefaultCompilerOptions : CompilerOptions :=
allowNonTsExtensions: true
allowJs: true
target: ts.ScriptTarget.Latest
moduleResolution: ts.ModuleResolutionKind.NodeJs
module: ts.ModuleKind.CommonJS
allowSyntheticDefaultImports: true
experimentalDecorators: true
fileCache : Record<string, any> := {}
createCompilerHost := (options: CompilerOptions, moduleSearchLocations : string[]) ->
fileExists := (fileName: string) : boolean ->
fileCache[fileName]?
readFile := (fileName: string) ->
fileCache[fileName]
Civet is essentially a tasteful superset of TypeScript.
See the documentation for examples of these and other features.
switch
can match patterns like [{type: "text", name}, ...rest]
data |> Object.keys |> console.log
equivalent toconsole.log(Object.keys(data))
&
shorthand|> await
, |> yield
, and |> return
(at end)x.map &.name
or x.map .name
→ x.map(a => a.name)
x.map &.profile?.name[0...3]
→ x.map(a => a.profile?.name.slice(0, 3))
x.map &.callback a, b
→ x.map($ => $.callback(a, b))
x.map !!&
→ x.map($ => !!$)
x.map &+1
→ x.map($ => $+1)
{foo()}
→ {foo: foo()}
, {props.foo}
→ {foo: props.foo}
{`${x}${y}`: z}
→ {[`${x}${y}`]: z}
data.{x,y}
or data{x,y}
→ {x: data.x, y: data.y}
{+debug, -live, !verbose}
→ {debug: true, live: false, verbose: false}
do
expressions, if
expressions, for
expressionsa := b
→ const a = b
, {a, b} := c
→ const {a, b} = c
a .= b
→ let a = b
a: number .= 5
→ let a: number = 5
a: number = 5
is the object literal {a: (number = 5)}
).@#id
→ this.#id
shorthand for private identifiersimport
shorthand: x from ./x
→ import x from "./x"
import
shorthand: import './x'
not at top levelawait import './x'
or inside a function) →import('./x')
import {x: y} from "./z"
→ import {x as y} from "./z"
. You can stillas
to be compatible with existing ES imports.export
shorthand: export x, y
→ export {x, y}
@( ... )
@ { ... }
<
as extends
shorthand///
Block RegExp like Python re.X
Inspired by solid-dsl discussions and jsx spec issues
<tag>
s or <>
s,>
isn't valid JSX text. For example, <For> (item) => ...
#foo
shorthand for id="foo"
;#"foo bar"
, #`foo ${bar}`
, #{expr}
.foo
shorthand for class="foo"
(but must be at least one space after.foo.bar
, ."foo bar"
, .`foo ${bar}`
, .{expr}
"civet react"
flag uses className
instead of class
+foo
shorthand for foo={true}
, -foo
/!foo
shorthand for foo={false}
{foo}
→ foo={foo}
, {foo: bar}
→ foo={bar}
,{...foo}
remains as is; methods and getters/setters work too....foo
shorthand for {...foo}
foo=bar
→ foo={bar}
, count=count()
→ count={count()}
,sum=x+1
→ sum={x+1}
, list=[1, 2, 3]
→ list={[1, 2, 3]}
[expr]={value}
→ {...{[expr]: value}}
"civet solid"
flag adds correct types for JSX elements and fragments."civet solid client"
(default) for client-only code,"civet solid server"
for server-only code (SSR only), or"civet solid client server"
for isomorphic code that runs on<!-- ... -->
→ {/* ... */}
.[mc]ts
→ .[mc]js
in imports (workaround for: https://github.com/microsoft/TypeScript/issues/37582):=
readonly class field initializer
class A
x := 3
class A {
readonly x = 3
}
void
return type, adding a trailing ;
orreturn
, or via the directive "civet -implicitReturns"
)x => ...
must become (x) => ...
x -> ...
=> x(function() ...)
in CoffeeScript and having ->
and =>
for(i of x) ...
defaults to const declaration → for(const i of x) ...
if x, y
is not allowed. But for i = 0, l = a.length; i < l; i++, i *= 2
is allowed.case
/when
instead becomes multiple conditions.y[0..x]
). This also implies that you can't access properties1..toString()
use 1.toString()
instead. When exponent follows a dot it is treated as a property access since an exponent1.e10
→ 1..e10
. The workaround is to add a trailing zero 1.0e10
or remove the dot before the exponent 1e10
.and
, or
, loop
, until
, unless
@@
instead of @
because @
is premium real estate and @id
→ this.id
, and @
is also static fields/methods, etc.
@@classDecorator
class X
@@methodDecorator
method() {}
when
inside switch automatically breaks and adds block scope.else
inside switch adds block scope.?
ex. x ? a : b
since x?
is the unary existential operator.:label
(except for special case $:
for Svelte)#!./node_modules/.bin/ts-node
console.log "hi"
Take a look at this detailed Civet // CoffeeScript comparision
Civet is not just one language; it can be configured in a variety of ways via directives to add or remove language features, or improve behavior in certain environments. See config documentation.
You have now been convinced that Civet is right for your current/next project. Here is how to set up your environment to get productive right away and have a Good Time℠.
Code coverage with c8 "just works" thanks to their source map integration and Civet's source maps.
package.json
"scripts": {
"test": "c8 mocha",
...
},
"c8": {
"extension": [
".civet"
]
},
"mocha": {
"extension": [
"civet"
],
"loader": [
"@danielx/civet/esm"
],
...
...
If you don't care for code coverage you can skip c8 (but it is so easy why not keep it?).
You can also add .js
and .ts
extensions if you want to mix and match! Even .coffee
will work if you require coffeescript/register
or add a loader for it.
Execute the tests
yarn test
Step 4: Enjoy!
Use the alpha version of Civet Language Server
The language server provides syntax highlighting, completions, hover documentation, symbols outline, red squigglies, and go to definition.
Q? Why can't I just use the built-in VSCode TypeScript LSP?
A: VSCode's built in TypeScript LSP can't resolve non .ts/.js
, not even with plugins. Maybe one day they'll allow for
plugins that let you adjust the resolver and insert a transpilation step but until then a separate language server is necessary.
Q? Sometimes the file outline disappears and the red squigglies are all in the wrong place and maybe a notification pops up about some kind of LSP error.
A: I'm sorry that happened to you but the Civet Language Server is still alpha and improving rapidly. Please let me know exactly what happened and I'll try to do better next time.
It may happen when there is a syntax error in your Civet file. You can check and see if it compiles using the CLI tool in the meantime.
Please do submit bug reports / feature requests.
I strongly recommend using esbuild for building / packaging your Civet project.
import esbuild from 'esbuild'
import civetPlugin from '@danielx/civet/esbuild-plugin'
esbuild.build({
...,
plugins: [
civetPlugin
]
}).catch(() => process.exit(1))
It's super fast and works great!
If you are so inclined, you can sponsor Civet on Open Collective.