prqlc-js:在 Node.js 与浏览器中调用 PRQL 编译器(WASM JavaScript 绑定完整指南)
2026/9/23 14:22:47 网站建设 项目流程
  • 后端

【免费下载链接】prql

PRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement

项目地址:https://gitcode.com/gh_mirrors/pr/prql
点击查看免费下载

导读

prqlc-js是 PRQL 项目官方提供的 JavaScript/TypeScript 绑定,它将 Rust 编写的prqlc编译器编译为 WebAssembly,从而让开发者能在 Node.js 服务端、现代浏览器以及各类前端框架中直接完成「PRQL → SQL」的编译。读完本文,你将掌握prqlcnpm 包的安装、compile与管道函数(prql_to_plpl_to_rqrq_to_sql)的使用、CompileOptions三个核心参数(targetformatsignature_comment)的语义与优先级,以及编译错误的结构化解析方法。

什么是 prqlc-js

PRQL(Pipelined Relational Query Language)是一种面向数据转换的现代语言,定位是"简单、强大、管道化的 SQL 替代品"。而prqlc是它的 Rust 编译器,负责把 PRQL 源码解析为中间表示并最终生成 SQL。prqlc-js正是这条编译管线在 JavaScript 生态中的桥接层:它通过wasm-bindgenprqlc编译成wasm32目标(见 Cargo.toml 中的cfg(target_family="wasm")依赖声明),再以 npm 包的形式对外分发。

从源码结构看(src/lib.rs),整个绑定层只暴露了一组薄封装函数,内部全部委托给 prqlc/prqlc/src/lib.rs 中 Rust 侧同名的编译入口,例如compile(Rust 侧实现在 lib.rs 第 189 行,内部依次执行parser::parsesemantic::resolve_and_lowersql::compile三个阶段)。因此 JS 调用方获得的能力与 Rust 原生 API 完全一致,只是输入输出以字符串 / JSON 的形式在 WASM 边界传递。

安装

prqlc-jsprqlc为包名发布在 npm registry 上(见 package.json),安装方式与其他 npm 包无异:

npm install prqlc

该包同时声明了 Node 与浏览器两套入口:

  • main指向dist/node/prqlc_js.js(Node.js CommonJS 入口);
  • browser指向dist/web/prqlc_js.js(浏览器 ESM 入口);
  • types指向dist/node/prqlc_js.d.ts(TypeScript 类型声明)。

files字段仅发布dist/**/*package.json,因此 npm 包体积只包含编译好的 WASM 产物与绑定代码。

快速开始:在 Node.js 中编译 PRQL

compile函数接受一段 PRQL 字符串,返回对应的 SQL 字符串。最直接的用法如下:

const prqlc = require("prqlc"); const sql = prqlc.compile(`from employees | select first_name`); console.log(sql);

compile的完整签名(TypeScript 视角):

function compile(prql_query: string, options?: CompileOptions): string;

当查询跨多行时同样直接传入模板字符串即可,PRQL 的管道语法天然适合这种写法:

const prqlc = require("prqlc"); const sql = prqlc.compile(` from employees select first_name `); console.log(sql);

对应地,Rust 侧compile的原型为fn compile(prql_query: &str, options: Option<CompileOptions>) -> Option<String>(见 src/lib.rs 第 9 行),Option参数在 JS 中表现为"可省略",省略时自动回退到CompileOptions::default()

使用 CompileOptions 控制编译行为

通过new prqlc.CompileOptions()构造选项对象,可以控制 SQL 生成的方言、格式与注释行为:

const opts = new prqlc.CompileOptions(); opts.target = "sql.mssql"; opts.format = false; opts.signature_comment = false; const sql = prqlc.compile(`from employees | take 10`, opts); console.log(sql);

三个字段的语义如下:

字段类型默认值说明
targetstring""(空字符串)目标 SQL 方言,如sql.mssqlsql.sqlitesql.duckdbsql.postgres;留空表示sql.any,此时使用查询头prql target:...指定的方言
formatbooleantrue是否将生成的 SQL 交给格式化器美化(拆分成多行、整理缩进与间距)
signature_commentbooleantrue是否在生成的 SQL 末尾追加编译器的签名注释(包含目标方言信息等)

这三个字段在 Rust 侧一一对应(src/lib.rs 第 60-74 行),并通过TryFrom<CompileOptions> for prqlc::Options(第 116-137 行)转换为编译器内部使用的 Options 结构。

target 的优先级与校验规则

target的处理逻辑在绑定层是"显式优先、留空回退":

  • opts.target为空字符串,则视为未设置,使用Target::default()(即sql.any),最终方言从 PRQL 查询头部的prql target:sql.xxx指令解析;
  • opts.target非空,则必须能被Target::from_str成功解析,否则直接抛出异常,而不是静默回退到通用 SQL——源码注释明确说明这是为了防止sql.postgrez之类的拼写错误悄悄编译成泛化 SQL(见 src/lib.rs 第 119-127 行)。

测试 tests/test_all.mjs 对这套规则做了完整覆盖:

  • CompileOptions显式设置target时,即使 PRQL 头部写了不同的方言,也以选项为准(CompileOptions should be preferred...,第 56-72 行);
  • 未设置target时,回退使用查询头方言(should treat an unset target as sql.any,第 83-96 行);
  • 传入未知方言如sql.postgrez会抛出包含该字符串的错误(第 74-81 行)。

Rust 侧Target枚举只支持Sql(Option<Dialect>)一种变体,Target::names()会生成sql.any加全部方言变体的完整列表(见 prqlc/prqlc/src/lib.rs 第 220-240 行)。这些方言名同时也是prqlc-js暴露的get_targets()函数的返回值来源(见 src/lib.rs 第 77-82 行),可用于在 UI 中动态渲染方言下拉框。

进阶 API:分段使用编译管线

除了端到端的compileprqlc-js还暴露了四个中间步骤函数,便于调试或在编译链路中注入自定义逻辑:

function prql_to_pl(prql_query: string): string; function pl_to_prql(pl_json: string): string; function pl_to_rq(pl_json: string): string; function rq_to_sql(rq_json: string): string;

对应 Rust 侧的调用链(src/lib.rs 第 20-55 行):

  • prql_to_pl(prql_query):PRQL 文本 → PL(管道语言中间表示)JSON。实现为prqlc::prql_to_pl后接json::from_pl序列化;
  • pl_to_prql(pl_json):PL JSON → PRQL 文本(反向格式化的逆操作),先json::to_pl反序列化再调用pl_to_prql
  • pl_to_rq(pl_json):PL JSON → RQ(关系查询中间表示)JSON,中间经过pl_to_rqjson::from_rq
  • rq_to_sql(rq_json):RQ JSON → SQL,此步骤以默认Options编译,不接收用户自定义选项。

测试中prql_to_pl对合法 PRQL 返回可被JSON.parse的字符串、对非法输入抛错(tests/test_all.mjs 第 99-107 行),验证了这些 API 的输入输出约定。从仓库结构看,PL 与 RQ 的具体数据结构定义分别在 prqlc/prqlc/src/ir/pl 与 prqlc/prqlc/src/ir/rq 目录下。

在浏览器中直接使用

prqlc-js的 WASM 产物同样面向浏览器设计。最轻量的方式是作为 ES Module 引入dist/web/prqlc_js.js,并先await init()完成 WASM 实例初始化:

<html> <head> <script type="module"> import init, { compile } from "./dist/web/prqlc_js.js"; await init(); const sql = compile("from employees | select first_name"); console.log(sql); </script> </head> <body></body> </html>

这种场景特别适合构建纯前端的数据工具:仓库中的 web/playground(在线 PRQL Playground)正是以file:../../prqlc/bindings/js的方式本地依赖该包(见 web/playground/package.json 第 21 行),配合 DuckDB WASM 在浏览器端完成 PRQL 编译与查询执行;项目官方文档 web/book/src/project/bindings/javascript.md 也直接通过{{#include}}引入了本 README,作为 JavaScript 绑定篇的正文。

在框架或打包器(Bundler)中使用

如果项目使用 Vite、Webpack 等打包工具,应从专门为 bundler 场景生成的入口导入,以获得正确的模块解析与 tree-shaking 支持:

import { compile } from "prqlc/dist/bundler"; const sql = compile(`from employees | select first_name`); console.log(sql);

这与package.jsonbrowser字段指向dist/web的约定相辅相成:nodewebbundler三个产物目录分别服务于三种运行时。

错误处理:结构化的编译诊断

与很多把错误编码进字符串的方案不同,prqlc-js将编译错误序列化为 JSON 数组后随异常抛出,调用方可以解析出结构化的诊断信息。错误对象的 TypeScript 接口如下:

interface ErrorMessage { /// Message kind. Currently only Error is implemented. kind: "Error" | "Warning" | "Lint"; /// Machine-readable identifier of the error code: string | null; /// Plain text of the error reason: string; /// A list of suggestions of how to fix the error hints: string[]; /// Character offset of error origin within a source file span: [number, number] | null; /// Annotated code, containing cause and hints. display: string | null; /// Line and column number of error origin within a source file location: SourceLocation | null; } /// Location within the source file. /// Tuples contain: /// - line number (0-based), /// - column number within that line (0-based), interface SourceLocation { start: [number, number]; end: [number, number]; }

关键字段速查:

  • kind:错误类别,当前仅实现Error
  • code:机器可读的错误码,例如E0001(语法错误);
  • reason:纯文本的错误原因描述;
  • hints:修复建议列表;
  • span:错误在源文件中的字符偏移区间;
  • display:带注解的代码片段(含原因与提示,多行文本);
  • location:错误起止的行号与列号(均为 0-based,即行号从 0 开始计数)。

捕获方式如下:

try { const sql = prqlc.compile(`from employees | foo first_name`); } catch (error) { const errorMessages = JSON.parse(error.message).inner; console.log(errorMessages[0].display); console.log(errorMessages[0].location); }

注意异常信息中的 JSON 结构带有一个inner键,实际的ErrorMessage[]数组位于JSON.parse(error.message).inner。这一序列化行为源于绑定层return_or_throw的实现——编译失败时调用wasm_bindgen::throw_str(&e.to_json()),将prqlc::ErrorMessages(内部为inner数组)整体 JSON 化后作为异常消息抛出(见 src/lib.rs 第 139-151 行)。

测试同样验证了错误结构:display字段包含换行(多行注解)、reason为单行纯文本、code可精确匹配E0001(tests/test_all.mjs 第 134-158 行)。此外,当启用console_error_panic_hookfeature(默认开启,见 Cargo.toml)时,WASM 内部的 panic 也会以console.error的形式输出,便于开发期排查。

本地开发与构建

在仓库内对prqlc-js做二次开发时,常用的命令如下:

npm run build

该命令会依次产出 Node、bundler、web 三个平台的构建产物,全部放入dist目录。实际构建脚本定义在 package.json,底层依次执行:

  • build:nodewasm-pack build --target nodejs --out-dir dist/node
  • build:webwasm-pack build --target web --out-dir dist/web
  • build:bundlerwasm-pack build --target bundler --out-dir dist/bundler

运行测试:

npm test

测试框架为 mocha + chai(见devDependencies),测试文件位于 tests/test_all.mjs,直接加载dist/node/prqlc_js.js进行断言。

加速开发循环:PROFILE 环境变量

默认情况下,每次构建都会对 WASM 二进制做优化(wasm-pack默认--release级别),即使底层代码没有变化也很耗时。开发阶段可以设置PROFILE环境变量为dev,以获得更快但优化程度较低的构建:

PROFILE=dev npm run build

其原理是构建脚本中--${PROFILE}的插值:PROFILE=dev时等价于wasm-pack build --dev

构建原理与注意事项

  • 绑定层基于wasm-pack生成(README 脚注同时指出wasm-pack维护活跃度有限,团队对替代方案持开放态度,并倾向裁减其部分特性以构建三个目标产物);
  • 项目在wasm-pack的常规用法之上叠加了一层 npm 封装:不采用"每个目标一个包"的推荐做法,而是把 Node、bundler、web 三个目标打包进同一个 npm 包dist子目录,构建指令写在buildscript 而非packscript 中(见 package.json)——这正是"一个包、三入口"设计得以实现的关键;
  • 绑定层的 Rust 代码整体受#![cfg(target_family = "wasm")]约束(src/lib.rs 第 1 行),意味着该 crate 只在 WASM 目标下编译,Rust 原生侧并不包含这些绑定代码;
  • CompileOptionsformatsignature_comment的默认值均为true(src/lib.rs 第 84-92 行),与 Rust 侧 Options::default() 保持一致,但绑定层强制使用DisplayOptions::Plain(无 ANSI 颜色)以适配终端之外的调用环境。

小结

prqlc-js用一个 npm 包同时覆盖了 Node.js、浏览器与打包器三种运行场景:compile负责端到端编译,四个管道函数暴露中间表示,CompileOptions提供方言/格式/注释的精细控制,而结构化错误对象则让 PRQL 的诊断信息可以被程序化消费。结合 tests/test_all.mjs 中覆盖方言优先级、未知方言报错、错误码解析等场景的用例,以及 web/playground 这一真实消费方,你可以放心地将它接入自己的数据工具链。

  • 后端

【免费下载链接】prql

PRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement

项目地址:https://gitcode.com/gh_mirrors/pr/prql
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询