Pular para o conteúdo

18 · Módulos

JavaScript e TypeScript com Node

Funções tipadas, módulos Node e callbacks assíncronos com extern.

Nesta página

Importe javascript ou typescript do plugin node. A assinatura usa tipos Zolo; o corpo usa a linguagem escolhida. Os dois providers compartilham Node dentro do processo e aceitam aliases como qualquer outro import.

Declare uma função pequena, agrupe funções TypeScript, capture um valor Zolo em um template JavaScript e importe node:crypto. Regex e templates aninhados mantêm seu significado nativo.

29-extern-node.zolo
Playground
// JavaScript and TypeScript use the same embedded Node runtime.

use plugin node::{javascript as js, typescript as ts}

extern js fn add(a: int, b: int) -> int {
    return a + b;
}

extern ts {
    fn normalize(text: str) -> str {
        return text.trim().toLowerCase();
    }

    fn root(value: float) -> float {
        return Math.sqrt(value);
    }
}

let original = "  Zolo  "
let greeting: str = extern js {
    return `Hi ${`${original.trim()}`}`;
}
let lexical: bool = extern js {
    // Braces and slashes in native literals remain inside this body.

    return /[{}\/]/.test("}") && `literal } ${original.trim()}`.endsWith("Zolo");
}

extern ts fn native_import() -> str {
    import { createHash } from "node:crypto";
    return createHash("sha256").update("Zolo").digest("hex").slice(0, 8);
}

print(add(20, 22))
print(normalize(original))
print(root(81.0))
print(greeting)
print(lexical)
print(native_import().len())

// expected:

// 42

// zolo

// 9

// Hi Zolo

// true

// 8

int e i64 chegam como bigint, preservando 64 bits. Escreva 1n ao somar um literal nativo a esses valores. Tipos de ponto flutuante usam number.

Transfira records, bytes e arrays com valores nulos; passe uma closure Zolo, receba uma função nativa e mantenha o estado de uma instância TypeScript.

30-extern-node-data.zolo
Playground
use plugin node::{typescript as ts}

struct Payload { value: int, optional: int?, items: [int] }

extern ts {
    fn echo(value: Payload) -> Payload {
        if (!Object.hasOwn(value, "optional") || value.optional !== null) {
            throw new Error("Nullable DTO field was lost");
        }
        return value;
    }
    fn nullable(values: [int?]) -> [int?] { return values; }
    fn empty() -> [int] { return []; }
    fn binary() -> bytes { return Buffer.from([0, 255, 42]); }
    fn call(callback: fn(int) -> int) -> int { return callback(41n); }
    fn factory(delta: int) -> fn(int) -> int { return value => value + delta; }
    fn counter() -> Any {
        class Counter {
            value = 0n;
            add(delta: bigint): bigint { this.value += delta; return this.value; }
        }
        return new Counter();
    }
}

let copy = echo(Payload {
    value: 9223372036854775807,
    optional: nil,
    items: [7, 8, 9],
})
print(copy.value)
print(copy.optional == nil)
print(copy.items.len())
let values = nullable([7, nil, nil])
print(values.len())
print(values[1] == nil)
print(empty().len())
print(binary().len())
print(call(|value: int| value + 1))
let increment = factory(1)
print(increment(41))
let live = counter()
print(live.add(40))
live.value = 41
print(live.add(1))
print(live.value)

// expected:

// 9223372036854775807

// true

// 3

// 3

// true

// 0

// 3

// 42

// 42

// 40

// 42

// 42

Importe funções e classes TypeScript, CommonJS com JSDoc e um módulo ESM com await de topo. Use um alias de classe, um método async e uma propriedade.

31-extern-node-imports.zolo
Playground
use plugin node::{javascript as js, typescript as ts}

extern ts from "./node_fixtures/tools.ts" use {twice, later, Counter as Count}
extern js from "./node_fixtures/common.cjs" use {twiceCjs}
extern js from "./node_fixtures/esm.mjs" use {fromEsm}

extern ts fn local_offset(value: int) -> int {
    import {offset} from './node_fixtures/offset.ts';
    return value + offset;
}

extern js fn local_common(value: int) -> int {
    const {twiceCjs} = require('./node_fixtures/common.cjs');
    return twiceCjs(value);
}

extern ts mod Tools {
    const {twiceCjs} = require('./node_fixtures/common.cjs');
    export function greet(name: string): string { return `Hello ${name}`; }
    export function twice(value: bigint): bigint { return twiceCjs(value); }
}

extern js mod CommonTools {
    const {twiceCjs} = require('./node_fixtures/common.cjs');
    /** @param {bigint} value @returns {bigint} */
    export function twice(value) { return twiceCjs(value); }
}

print(twice(21))
print(await later(41))
let counter = Count(40)
print(counter.add(1))
print(await counter.addLater(1))
print(counter.value)
print(counter.total)
print(twiceCjs(21))
print(fromEsm(41))
print(Tools.greet("Zolo"))
print(Tools.twice(21))
print(CommonTools.twice(21))
print(local_offset(41))
print(local_common(21))
let value = 41
let captured: int = extern ts {
    import {offset} from './node_fixtures/offset.ts';
    return value + offset;
}
print(captured)

// expected:

// 42

// 42

// 41

// 42

// 42

// 42

// 42

// 42

// Hello Zolo

// 42

// 42

// 42

// 42

// 42

Um callback que retorna Future vira Promise e pode voltar a uma função Node assíncrona. Combine chamadas, capture erros, cancele uma tarefa e receba um timer.

32-extern-node-async.zolo
Playground
use plugin node::{typescript as ts}
use std::async::Promise

extern ts async fn later(value: int) -> int {
    await new Promise(resolve => setTimeout(resolve, 1));
    return value + 1n;
}

extern ts async fn transform(value: int, callback: fn(int) -> Future<int>) -> int {
    await new Promise(resolve => setTimeout(resolve, 1));
    return await callback(value);
}

extern ts async fn fail() -> int {
    await new Promise(resolve => setTimeout(resolve, 1));
    throw new Error("Node failed");
}

extern ts async fn wait_for_cancel() -> int {
    await new Promise(resolve => setTimeout(resolve, 60000));
    return 0n;
}

extern ts async fn schedule(callback: fn(int) -> int) -> int {
    setTimeout(() => callback(42n), 10);
    return 42n;
}

async fn nested(value: int) -> int {
    return await later(value)
}

async fn reject(value: int) -> int {
    sleep 1ms
    panic("callback failed")
    return value
}

async fn cancel_soon(future: Future<int>) -> bool {
    sleep 5ms
    future.cancel()
    return future.is_done()
}

let values = await Promise.all([
    transform(41, nested),
    transform(42, nested),
])
print(values[0])
print(values[1])
print(try { await transform(0, reject) } catch error { "callback caught" })
print(try { await fail() } catch error { "Node caught" })
let pending = wait_for_cancel()
print(await cancel_soon(pending))
let background: [int] = []
print(await schedule(|value: int| {
    background.push(value)
    return value
}))
sleep 40ms
print(background[0])

// expected:

// 42

// 43

// callback caught

// Node caught

// true

// 42

// 42

Callbacks executam na thread Zolo. Quando o callback precisa aguardar outra função Node, declare fn(...) -> Future<T> e aguarde a Promise no corpo nativo. Isso permite que o event loop continue avançando durante a chamada.

O adapter experimental usa a biblioteca compartilhada Node 26.10.0 para Windows x64. Distribua node-runtime/ junto do plugin ou configure ZOLO_NODE_HOME. O compilador TypeScript e seus tipos ficam embutidos no plugin. Pacotes npm precisam das dependências normais da aplicação. O editor analisa o texto atual sem executar código do usuário. Outros sistemas ainda aguardam validação.

Veja também

Índice global

Encontre seu caminho pela Zolo

Experimente uma ideia

Comece por aqui

9 resultados

9 resultados

enpt-br