☰
Wing 语言本地模块导入实战:`bring “./file.w“` 语法、编译产物与 Hangar 测试快照验证
2026/10/15 11:53:38 网站建设 项目流程
  • 编程语言
  • 编译器
  • 云原生
  • 语言运行时

【免费下载链接】wing

A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡

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

本文围绕 Wing 语言(The Wing Programming Language,本仓库为 wing 的镜像仓库)的本地模块导入机制展开,以仓库测试套件 Hangar 生成的 bring_local.test.w_test_sim.md 快照为核心线索,结合测试源码 bring_local.test.w 与对应编译快照 bring_local.test.w_compile_tf-aws.md,讲解如何在 Wing 中通过bring引入同项目内的.w文件模块、跨文件复用 class/struct/enum/interface 等类型,并剖析wing test在 sim 平台上的执行与快照生成原理。读完本文,你将掌握 Wing 本地模块的导入语法、可见性规则、extern与子模块的使用方式,以及如何阅读和验证 Hangar 测试快照。

一、快照文档是什么:一条测试通过记录

bring_local.test.w_test_sim.md是 Hangar 测试工具链为tests/valid/bring_local.test.w生成的sim 平台测试快照,其正文只有一段stdout.log:

pass ─ bring_local.test.wsim » root/Default/test:add data to store pass ─ bring_local.test.wsim » root/Default/test:greet Tests 2 passed (2) Snapshots 1 skipped Test Files 1 passed (1) Duration <DURATION>

这短短几行输出揭示了三个关键信息:

  1. 文件命名规范:*.test.w是 Wing 的入口测试文件(详见 generated_test_targets.ts 中的isEntrypointFile判定:*.test.w、*.main.w、main.w均被视为入口文件,只有入口文件才允许包含test块);_test_sim表示测试目标为sim(模拟器)平台,与此对应的编译目标为_compile_tf-aws。
  2. 测试用例:该文件定义了test:add data to store与test:greet两个用例,root/Default/是 Wing 应用树(application tree)中测试资源std.Test在模拟器中的构造路径。
  3. 结果摘要:Tests 2 passed (2)表明两个测试全部通过,Snapshots 1 skipped说明该快照未做其他快照比对,Duration <DURATION>是 Hangar 在生成快照时对运行时长的脱敏占位符。

二、测试源码全景:本地模块导入的典型用法

快照背后的被测程序是 bring_local.test.w,其开头集中展示了 Wing 本地模块导入的几种形态:

bring "./store.w" as file1; bring "./subdir/subfile.w" as file2; bring "./subdir/empty.w" as file3; bring math; bring expect;
  • bring "./store.w" as file1;:以相对路径导入同目录下的.w文件,并以file1作为命名空间别名。这是 Wing 本地模块导入的核心语法,路径相对于当前文件解析,扩展名.w可以省略。
  • bring "./subdir/subfile.w" as file2;:支持导入子目录中的文件,说明模块可以是任意层级的相对路径。
  • bring "./subdir/empty.w" as file3;:即使目标文件是空文件(empty.w 内容为空)也能正常导入,空模块是合法模块。
  • bring math;、bring expect;:导入内置标准库模块(math、expect),不需要路径。

依据官方语言参考 05-language-reference.md 的 Module System 章节,bring表达式只允许出现在文件顶部、任何其他代码之前(注释除外),并且是 Wing 中唯一允许跨文件"导入"函数、类与变量的语法。本地文件导入的具体写法为:

bring "./my-module.w" as myModule;

同时支持以目录为模块导入——模块将包含该目录下所有文件中公开的类型,子目录则按对应名称暴露:

bring "./my-module" as myModule; new myModule.submodule.MyClass(); // 来自 ./my-module/submodule/my-class.w

三、跨文件复用:class、struct、enum、interface 与同名类

bring_local.test.w的剩余部分逐一验证了本地模块导入后可以复用的类型:

3.1 实例化其他文件的类

let store = new file1.Store(); let q = new file2.Q(); expect.equal(file2.Q.preflightGreet("foo"), "Hello foo");

file1.Store来自 store.w 中定义的pub class Store,其构造器内部创建了一个cloud.Bucket并通过cloud.OnDeploy在部署时写入初始化数据;file2.Q来自 subfile.w,静态方法preflightGreet由extern实现。

3.2 其他文件的 struct、enum、interface

let s = file1.Point { x: 1, y: 2 }; // struct let c = file1.Color.BLUE; // enum assert(c != file1.Color.RED); class Triangle impl file1.Shape { // interface pub area(): num { return 1; } }

对应地,store.w 用pub声明了enum Color(RED/GREEN/BLUE)、struct Point(x: num; y: num;)和interface Shape(area(): num;)。这里验证了本地模块可以完整导出枚举、结构体与接口,且接口可以被入口文件中的类实现——这是 Wing 类型系统中模块间交互的重要能力。

3.3 同名类的隔离

// classes can have the same name as classes in other modules class Util {}

store.w 中同样定义了pub class Util,而入口文件也定义了class Util。由于模块以命名空间(file1)隔离,二者互不冲突。编译快照印证了这一点:bring_local.test.w_compile_tf-aws.md 中入口文件的Util定义在preflight.cjs内,而 store.w 的Util则位于独立的preflight.store-2.cjs产物中。

3.4 extern:用 TypeScript 实现本地模块方法

file2.Q所在的 subfile.w 展示了extern修饰符的用法:

bring math; pub class Q { pub extern "./util.ts" static inflight greet(name: str): str; pub extern "./util.ts" static preflightGreet(name: str): str; }

extern "<javascript module path>"可挂载到类的方法声明上,指示该方法由相对路径的 JavaScript/TypeScript 模块实现(语言参考见 05-language-reference.md)。实现文件 util.ts 通过util.extern类型声明保证与 Wing 侧签名一致:

import type extern from "./util.extern"; export const greet: extern["greet"] = async (name) => { return "Hello " + name; } export const preflightGreet: extern["preflightGreet"] = (name) => { return "Hello " + name; }

编译快照中的 inflight.Q-2.cjs 显示该方法最终被编译为require("../../../subdir/util.ts")"greet"的调用,即运行时通过 Node 的require加载外部 TS 模块。

四、测试用例与编译产物:从 Wing 源码到可执行代码

4.1 两个测试用例

test "add data to store" { store.store("foo"); } test "greet" { expect.equal(file2.Q.greet("bar"), "Hello bar"); }
  • add data to store:在 inflight 环境调用store.store("foo"),将数据写入 Store 内部的 Bucket。
  • greet:调用file2.Q.greet("bar")(extern 实现的 inflight 静态方法)并用expect.equal断言结果为"Hello bar"。

4.2 inflight 闭包的编译结果

测试块会被编译为 inflight 闭包,见编译快照中的 inflight.$Closure1-3.cjs 与 inflight.$Closure2-3.cjs:

module.exports = function({ $store }) { class $Closure1 { async handle() { (await $store.store("foo")); } } return $Closure1; }
module.exports = function({ $expect_Util, $file2_Q }) { class $Closure2 { async handle() { (await $expect_Util.equal((await $file2_Q.greet("bar")), "Hello bar")); } } return $Closure2; }

闭包以工厂函数接收 lift 上来的对象引用($store、$file2_Q、$expect_Util),体现了 Wing 的lift 机制——preflight 对象被显式提升到 inflight 作用域后才能在运行时访问。

4.3 Store 类及其 AWS 资源

preflight.store-2.cjs 是 store.w 的编译产物:Store构造器内部new cloud.Bucket()与cloud.OnDeploy均被实例化为真实构造树节点。而 main.tf.json 则展示了 tf-aws 目标下的 Terraform JSON 产物——一个aws_s3_bucket(Store 的 Bucket)、一个aws_lambda_function(OnDeploy 的部署函数,runtimenodejs20.x、架构arm64、超时 60 秒)以及配套的 IAM 角色、CloudWatch 日志组等资源。这说明即使测试本身运行在 sim 平台,Wing 仍然能为同一源码产出完整的云基础设施定义。

4.4 入口文件的 preflight 组装

preflight.cjs 展示了本地模块在编译产物中的加载方式:

const file1 = $helpers.bringJs(`${__dirname}/preflight.store-2.cjs`, $preflightTypesMap); const file2 = $helpers.bringJs(`${__dirname}/preflight.subfile-3.cjs`, $preflightTypesMap); const file3 = $helpers.bringJs(`${__dirname}/preflight.empty-1.cjs`, $preflightTypesMap);

每个被bring的本地.w文件都会被编译为独立的preflight.<name>-<n>.cjs模块,再通过 SDK 的bringJs帮助函数加载。其实现位于 packages/@winglang/sdk/src/helpers.ts:除了执行require,还会把被导入模块的$preflightTypesMap合并进当前模块的类型映射,从而让编译器生成的类型解析(如globalThis.$ClassFactory.new("examples-valid.Store", file1.Store, this, "Store"))在运行时可用。这正是"本地模块导入"在运行时层面的落地方式。

五、快照如何生成:Hangar 的测试流水线

bring_local.test.w_test_sim.md由工具链 Hangar 自动生成,相关源码位于 generated_test_targets.ts:

  • 测试快照(testTest,L86-L122):以["sim"]为平台、["test"]为参数运行wing test,将WING_HIDE_CONTAINER_LOGS=1注入环境,把 stdout 写入fileMap["stdout.log"],再调用createMarkdownSnapshot生成*_test_sim.md。这就是本文开头那段 stdout 快照的来源。
  • 编译快照(compileTest,L12-L84):以["tf-aws"]为平台运行wing compile,收集target/<name>.tfaws/.wing目录下匹配preflight.、inflight.、extern/、proc、.Struct.js前缀的文件以及main.tf.json,统一生成*_compile_tf-aws.md。快照还包含"禁止绝对路径 require"的校验(L69-L71),确保产物可移植。

因此,test_corpus下同一测试名对应的_test_sim.md与_compile_tf-aws.md是一对互补证据:前者证明行为正确性,后者证明基础设施产物符合预期。

六、本地模块导入的更多形态:归一化、目录与别名

除了bring_local.test.w,仓库中还有一组围绕bring的专项测试,可作为扩展阅读:

  • 路径归一化:bring_local_normalization.test.w 验证同一个文件通过不同相对路径(如./subdir/bar.w与./bar.w、./baz.w与../baz.w)导入时,Wing 能正确解析为同一个模块,不会产生重复定义。
  • 目录导入:bring_local_dir.test.w 验证bring "./subdir2" as subdir;的目录级导入——目录下所有文件公开类型平铺,子目录类型通过subdir.inner.Widget访问。
  • 命名别名:bring_alias.test.w 验证内置模块也可用as起别名(bring fs as stdFs;),同一模块多次以不同别名导入不冲突。

七、小结与上手路径

从快照bring_local.test.w_test_sim.md出发,可以完整还原 Wing 本地模块导入的闭环:

  1. 语法:bring "./path/to/file.w" as ns;导入相对路径模块,bring dir;导入目录,bring mod;导入内置库(详见 语言参考 · Module System)。
  2. 可见性:被导入文件中的类型须以pub声明(如 store.w 中的pub class / pub enum / pub struct / pub interface),导入后经命名空间隔离访问,同名类互不干扰。
  3. 编译与运行:每个本地模块编译为独立preflight.*.cjs,经 bringJs 加载并合并类型映射;extern方法在运行时通过require调用外部 JS/TS 实现。
  4. 验证:wing test(sim 平台)与wing compile(tf-aws 平台)由 Hangar 自动执行并产出 Markdown 快照,作为行为与产物的回归基准。

如果你想在本地复现这一验证过程,可在安装 Wing CLI 后,于tests/valid目录执行wing test bring_local.test.w(sim 运行)与wing compile bring_local.test.w -t tf-aws(生成 Terraform 产物),再对照 bring_local.test.w_test_sim.md 与 bring_local.test.w_compile_tf-aws.md 两份快照核对输出。

  • 编程语言
  • 编译器
  • 云原生
  • 语言运行时

【免费下载链接】wing

A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡

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

相关推荐

上一篇:ulw-plan CLEAR 意图路径解析:oh-my-openagent 中如何以最少提问产出决策完备的工作计划
下一篇:90DaysOfDevOps 实战:在 Windows 上配置 Go 开发环境并完成 Hello World(Day 8)

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

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

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

立即咨询