资讯动态

Wing 语言工厂模式实战:从 `BucketFactory` 源码到 tf-aws 编译产物的完整解析

发布时间:2026/10/15 5:54:06 来源:尧图企业网站定制
编程语言编译器云原生语言运行时【免费下载链接】wingA 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 语言中工厂模式Factory Pattern是一种常用的资源组织方式通过类的静态方法封装cloud.Bucket等云资源的创建逻辑并在调用方把this作为作用域scope传入实现资源的集中管理与复用。本文以仓库中的factory.test.w测试用例为骨架结合 Hangar 测试框架生成的tf-aws编译快照逐层剖析 Wing 源码如何被编译为 JavaScript 闭包inflight与 Terraform JSON 配置并解读闭包捕获lift机制、CORS 默认配置与快照测试的运行原理。读完本文你将能读懂任何 Wing 编译产物的结构与含义并掌握工厂模式的正确写法。一、测试用例工厂模式在 Wing 中的完整写法文章的主角是仓库中的两个文件被测源码 tests/valid/factory.test.w一段合法valid测试用例验证工厂模式可以正常工作编译快照 tools/hangar/snapshots/test_corpus/valid/factory.test.w_compile_tf-aws.mdWing CLI 以tf-aws为目标编译该源码后生成的完整产物快照。先看被测源码的完整内容bring cloud; bring constructs as constructs; bring expect; class BucketFactory { pub static makeBucket(scope: constructs.IConstruct): cloud.Bucket { let bucket new cloud.Bucket() in scope; // apply customizations to bucket... return bucket; } } // Test that we can use the factory pattern by passing top-level this let bucket BucketFactory.makeBucket(this); log(nodeof(bucket).path); test can use bucket { bucket.put(hello, world); expect.equal(bucket.list().length, 1); } test can use other bucket { bucket.put(yo, sup); expect.equal(bucket.list().length, 1); }这段代码包含四个关键的语言特性bring引入模块bring cloud引入 Wing 的标准云资源库bring constructs as constructs以别名constructs引入 CDK 生态的 constructs 包bring expect引入断言工具库对应 SDK 中的expect模块。静态工厂方法pub static makeBucket(scope: constructs.IConstruct): cloud.Bucket是一个公开静态方法参数是类型为constructs.IConstruct的作用域对象返回值类型为cloud.Bucket。作用域内创建资源new cloud.Bucket() in scope表示在传入的scope之下创建 Bucket 资源——这正是 Wing 应用树application tree挂载节点的方式。// apply customizations to bucket...注释表明工厂方法预留了对 Bucket 做自定义配置的扩展点。顶层this与test块在文件顶层this指向应用根节点BucketFactory.makeBucket(this)把根节点作为 scope两个test块随后对工厂创建出的同一个 Bucket 执行写入与断言且两个测试各自独立、互不干扰。代码中的log(nodeof(bucket).path)会在编译运行时打印出 Bucket 在应用树中的路径而模拟器sim测试快照恰好记录了这一输出。二、编译快照逐层拆解Wing 源码变成了什么当以tf-aws为目标执行wing compile后Hangar 会收集编译输出目录中的关键文件见下节快照如何生成并按文件名分节写入 Markdown 快照。factory.test.w_compile_tf-aws.md共记录了 4 类产物三个 inflight 闭包.cjs、一份 Terraform JSONmain.tf.json和一份预检代码preflight.cjs。2.1 inflight 闭包$Closure1与$Closure2两个test块中的内联闭包被编译成独立的 CommonJS 模块。以inflight.$Closure1-1.cjs为例use strict; const $helpers require(winglang/sdk/lib/helpers); const $macros require(winglang/sdk/lib/macros); module.exports function({ $bucket, $expect_Util }) { class $Closure1 { constructor($args) { const { } $args; const $obj (...args) this.handle(...args); Object.setPrototypeOf($obj, this); return $obj; } async handle() { (await $bucket.put(hello, world)); (await $expect_Util.equal((await $bucket.list()).length, 1)); } } return $Closure1; }可以观察到几个重要事实模块工厂函数module.exports function({ $bucket, $expect_Util })表明闭包通过参数注入被捕获lift的外部对象——$bucket是预检阶段创建的那个 Bucket 的客户端$expect_Util是断言库的工具类。这正是 Wing 闭包捕获机制在产物层面的体现。可调用包装构造函数内const $obj (...args) this.handle(...args); Object.setPrototypeOf($obj, this); return $obj;把闭包实例包装成可直接调用的函数对象。异步执行handle()是async函数await $bucket.put(...)与await $bucket.list()表明在飞行inflight阶段所有云资源调用都是异步的。$Closure2-1.cjs结构完全一致只是写入的数据不同yo/sup。这印证了两个test块被编译为两个相互独立的闭包拥有各自的 lift 映射。而inflight.BucketFactory-1.cjs几乎为空——class BucketFactory {}没有任何成员。原因在于BucketFactory只有预检preflight阶段的静态方法不包含任何飞行阶段代码因此其 inflight 表示只是一个空壳。2.2 main.tf.jsonTerraform 资源定义编译产物中与 AWS 部署直接相关的是main.tf.json。将其与 SDK 中 packages/winglang/sdk/src/target-tf-aws/bucket.ts 的实现对照可以完整还原资源是如何生成的{ //: { metadata: { backend: local, stackName: root }, outputs: {} }, provider: { aws: [ {} ] }, resource: { aws_s3_bucket: { Bucket: { //: { metadata: { path: root/Default/Default/Bucket/Default, uniqueId: Bucket } }, bucket_prefix: bucket-c88fdc5f-, force_destroy: false } }, aws_s3_bucket_cors_configuration: { Bucket_CorsConfiguration-1357ca3a_A4CCA40A: { //: { metadata: { path: root/Default/Default/Bucket/CorsConfiguration-1357ca3a, uniqueId: Bucket_CorsConfiguration-1357ca3a_A4CCA40A } }, bucket: ${aws_s3_bucket.Bucket.id}, cors_rule: [ { allowed_headers: [*], allowed_methods: [GET, POST, PUT, DELETE, HEAD], allowed_origins: [*], expose_headers: [], max_age_seconds: 0 } ] } } } }这份 JSON 揭示了三个值得注意的细节唯一 S3 存储桶aws_s3_bucket.Bucket只有一个实例。虽然测试用例创建了一个BucketFactory且调用了两次makeBucket但实际只调用了一次let bucket BucketFactory.makeBucket(this)两个test共享同一个 Bucket——这与factory.test.w源码的语义完全一致。bucket_prefix: bucket-c88fdc5f-表明存储桶名称由编译器生成的哈希前缀唯一化force_destroy: false是默认值。CORS 配置默认开启aws_s3_bucket_cors_configuration资源由addCorsRule生成。在 target-tf-aws/bucket.ts 中构造函数在props.cors ?? true为真时调用addCorsRule并默认采用 SDK 定义的 DEFAULT_BUCKET_CORS_CONFIGURATIONallowedHeaders: [*]、allowedOrigins: [*]、五种 HTTP 方法、maxAge为 0 秒。快照中的cors_rule数组正是该默认配置被Lazy.anyValue惰性求值后序列化的结果。资源 ID 中的CorsConfiguration-1357ca3a后缀来自this.node.addr.slice(-8)取节点地址的后 8 位。跨平台一致性在 CDK 平台实现 platform-awscdk/src/bucket.ts 中同样的默认 CORS 逻辑通过this.bucket.addCorsRule({...})落到底层 S3 Bucket 上参数映射保持一致如maxAge从Duration换算为秒。这说明 Wing 的一次编写、多平台部署理念在底层确实由各平台实现共同支撑。2.3 preflight.cjs应用装配代码preflight.cjs是理解 Wing 编译模型的关键它展示了预检阶段的全部逻辑use strict; const $stdlib require(winglang/sdk); const $macros require(winglang/sdk/lib/macros); const $platforms ((s) !s ? [] : s.split(;))(process.env.WING_PLATFORMS); const $outdir process.env.WING_SYNTH_DIR ?? .; const $wing_is_test process.env.WING_IS_TEST true; const std $stdlib.std; const $helpers $stdlib.helpers; const $extern $helpers.createExternRequire(__dirname); const $types require(./types.cjs); const $PlatformManager new $stdlib.platform.PlatformManager({platformPaths: $platforms}); class $Root extends $stdlib.std.Resource { constructor($scope, $id) { super($scope, $id); ... class BucketFactory extends $stdlib.std.Resource { constructor($scope, $id, ) { super($scope, $id); } static makeBucket($scope, scope) { const bucket globalThis.$ClassFactory.new(winglang/sdk.cloud.Bucket, cloud.Bucket, scope, Bucket); return bucket; } ... } ... const bucket (BucketFactory.makeBucket(this, this)); console.log($helpers.nodeof(bucket).path); globalThis.$ClassFactory.new(winglang/sdk.std.Test, std.Test, this, test:can use bucket, new $Closure1(this, $Closure1)); globalThis.$ClassFactory.new(winglang/sdk.std.Test, std.Test, this, test:can use other bucket, new $Closure2(this, $Closure2)); } } const $APP $PlatformManager.createApp({ outdir: $outdir, name: factory.test, rootConstruct: $Root, isTestEnvironment: $wing_is_test, entrypointDir: process.env[WING_SOURCE_DIR], rootId: process.env[WING_ROOT_ID] }); $APP.synth();这段代码清晰地展示了 Wing 编译器的降级lowering策略类降级源码中的class BucketFactory被降级为class BucketFactory extends $stdlib.std.Resource静态方法makeBucket($scope, scope)保留内部通过globalThis.$ClassFactory.new(winglang/sdk.cloud.Bucket, ...)这一间接工厂创建资源这与源码new cloud.Bucket() in scope一一对应。类定义被内联在$Root构造函数体内因为 Wing 的类型可以依赖闭包捕获。this的传递源码中的BucketFactory.makeBucket(this)在产物中变成BucketFactory.makeBucket(this, this)——第一个this是作用域参数第二个this是工厂内部创建资源时使用的 scope两者都指向应用根节点。lift 映射$Closure1的_liftMap明确列出了handle方法需要 lift 的条目[bucket, [put, list]]对 Bucket 的put/list方法以及[winglang/sdk.expect.Util, [equal]]。这正是 inflight 闭包中$bucket、$expect_Util参数的来源。同时$helpers.nodeof(this).root.$preflightTypesMap维护了资源类型的注册表。测试注册两个test块被编译为globalThis.$ClassFactory.new(winglang/sdk.std.Test, ..., test:can use bucket, new $Closure1(...))形式的注册调用把测试名与对应闭包绑定。平台管理$PlatformManager.createApp(...)根据WING_PLATFORMS环境变量创建应用并调用$APP.synth()完成产物合成。快照中//# sourceMappingURLpreflight.cjs.map表明编译器同时生成了 source mapHangar 收集产物时将其排除。三、快照如何生成Hangar 测试框架的运行机制这份快照并非手工编写而是由仓库内 Hangar 测试工具自动生成并校验的。理解其生成链路才能正确复现与解读。3.1 测试用例的自动生成tools/hangar/src/generate_tests.ts 会扫描tests/valid等目录下的所有.w文件为每个文件生成一个 vitest 测试文件。对factory.test.w而言生成的是两个测试test(wing compile -t tf-aws, async () { await compileTest(sourceDir, factory.test.w, env, true, false, []); }); test(wing test -t sim, async () { await testTest(sourceDir, factory.test.w, env, false); });生成逻辑见 generate_tests.ts编译测试默认includeJavaScriptInSnapshots: true因此快照包含.cjs产物模拟器测试则不收集 JS 文件。3.2 compileTest编译、收集、清洗、落盘tools/hangar/src/generated_test_targets.ts 中的compileTest是编译快照的完整流水线执行编译runWingCommand调用wing compile并指定-t tf-aws平台由[tf-aws]数组给出产物输出到源码目录下的target/factory.test.tfaws。runWingCommand位于 utils.ts执行时会附加--no-update-check、--no-analytics参数并校验退出码——编译失败即测试失败。收集产物入口文件.test.w结尾的文件编译后main.tf.json被读取并经过 sanitize_json_paths 清洗terraform块与 cdktf 版本号被删除、绝对路径资产被替换为ASSET_KEY/ASSET_SOURCE/SOURCE占位符以保证快照在不同机器上可复现。随后遍历target/factory.test.tfaws/.wing目录只收集文件名包含preflight.、inflight.、extern/、proc、.Struct.js的文件排除.mapsource map。落盘快照createMarkdownSnapshot 把所有收集到的文件按字典序排序逐个写入 Markdown 的## 文件名 fenced code block快照路径为__snapshots__/test_corpus/valid/factory.test.w_compile_tf-aws.md并用expect(md).toMatchFileSnapshot(...)与已有快照比对——任何产物变化都会导致测试失败从而保证编译输出的稳定性。值得注意compileTest中还有一道绝对路径守卫——若产物中出现require(绝对路径...)会直接抛错确保快照与产物都不依赖机器本地的绝对路径。3.3 模拟器测试快照运行结果验证与编译快照配套的还有 tools/hangar/snapshots/test_corpus/valid/factory.test.w_test_sim.md它记录了wing test -t sim的 stdoutroot/Default/Bucket pass ─ factory.test.wsim » root/Default/test:can use bucket pass ─ factory.test.wsim » root/Default/test:can use other bucket Tests 2 passed (2) Snapshots 1 skipped Test Files 1 passed (1) Duration DURATION这份快照验证了两个关键点工厂创建成功第一行root/Default/Bucket正是源码中log(nodeof(bucket).path)的输出说明在顶层this作用域下创建的 Bucket 路径为root/Default/Bucket应用树的根 → Default 阶段 → Bucket 节点。两个测试均通过两个test块在模拟器中分别执行putlistexpect.equal断言且全部 pass。Duration DURATION是 sanitizeOutput 归一化后的结果——它把运行时长、时间戳、端口、绝对路径等环境相关输出统一替换为占位符如Duration \dm[\d.]s→Duration DURATION使快照在不同运行环境间保持一致。模拟器测试由 generated_test_targets.ts 的testTest驱动仅在入口文件.test.w结尾上运行并在WING_HIDE_CONTAINER_LOGS1环境变量下执行收集 stdout/stderr 后同样走 Markdown 快照比对。四、工厂模式背后的 Wing 核心机制透过这份快照可以提炼出工厂模式在 Wing 中成立的三个底层机制4.1 静态方法与作用域传递工厂方法makeBucket(scope)的scope参数类型是constructs.IConstruct——这与 SDK 中 Bucket 类 的构造函数签名constructor(scope: Construct, id: string, props: BucketProps {})一致任何资源都必须挂载在某一个构造节点之下。调用方把this传入资源就被挂到调用方的子树中从而保证应用树结构完整、资源 ID 不冲突。这正是注释// Test that we can use the factory pattern by passing top-level this想要验证的场景。4.2 闭包捕获lift与依赖注入test块内对bucket的引用在编译后被转换为对闭包构造函数的参数注入module.exports function({ $bucket, $expect_Util })。lift 清单_liftMap精确声明了每个 inflight 方法用到了哪些外部对象、哪些方法put、list、equal编译器据此在 cloud/bucket.ts 定义的IBucketClient.put(key, body)等接口之上生成安全的运行时访问代码。工厂返回的资源与其他方式创建的资源在这一环节没有任何区别——闭包捕获只关心对象本身不关心它来自工厂还是直接new。4.3 编译产物与测试即文档Hangar 的 Markdown 快照本质上是一份可执行、可回归校验的文档Wing 源码 → preflight.cjs → inflight 闭包 → main.tf.json 的完整映射链被固化在 tools/hangar/snapshots/test_corpus/valid/ 目录下任何编译器行为变化都会在 CI 中被快照比对捕获。因此阅读factory.test.w_compile_tf-aws.md等同于阅读Wing 编译器针对工厂模式当前时刻的真实行为说明书。五、如何复现与进一步探索仓库是只读的你可以在本地以如下方式复现本文全部结论查看源码与快照对照阅读 tests/valid/factory.test.w、编译快照 与 模拟器快照。运行测试在安装好依赖的仓库内执行 Hangar 的相关 vitest 用例factory.test.w.test.ts由 generate_tests.ts 生成于tools/hangar/test_corpus/valid/或直接使用wing compile -t tf-aws tests/valid/factory.test.w与wing test -t sim tests/valid/factory.test.w复现产物与运行结果。注意快照中的路径、时长等字段是经过sanitizeOutput归一化的本机运行输出的具体时长、哈希值可能与快照不同这属正常现象。深入源码进一步阅读 SDK Bucket 定义、tf-aws 平台实现 与 awscdk 平台实现以及 Hangar 的 生成器、测试目标 与 工具函数。六、小结factory.test.w及其编译快照构成了一个完整的学习闭环源码展示了静态工厂方法 顶层this传递 共享资源的多测试复用这一惯用写法compile_tf-aws快照证明其被正确降级为预检代码、独立的 inflight 闭包与一份可直接交给 Terraform 的 AWS 资源定义含默认 CORS 规则test_sim快照则验证了在模拟器中两个测试均真实通过。对 Wing 开发者而言这份快照既是最好的编译器行为说明书也是衡量自己能否读懂编译产物的试金石——当你能够解释_liftMap中的每一个条目、CorsConfiguration-1357ca3a后缀的来源以及$ClassFactory.new的作用时你对 Wing 预检/飞行模型的掌握就已足够深入。赞分享编程语言编译器云原生语言运行时【免费下载链接】wingA 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 语言 extern 机制深探从 extern_implementation.test.w 到 tf-aws 编译产物全解析Wing 语言 extern 机制深探从 extern_implementation.test.w 到 tf aws 编译产物全解析 本篇技术指南以 Wing编程语言编译器云原生语言运行时Wing 语言 cloud.Api 路径变量Path Variables实战从 :name 语法到 tf-aws 编译产物全解析Wing 语言 cloud.Api 路径变量Path Variables实战从 :name 语法到 tf aws 编译产物全解析 本篇文章以 Wing 仓编程语言编译器云原生语言运行时Wing 云编程语言的隐式 await 机制从 inflight 异步模型到 tf-aws 编译产物全解析Wing 云编程语言的隐式 await 机制从 inflight 异步模型到 tf aws 编译产物全解析 导读 本文以开源仓库 wing 中 hangar编程语言编译器云原生语言运行时上一篇SmartGPT插件系统深度解析打造你的专属AI工具箱下一篇Amazing-QR与Excel集成批量生成二维码插件终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑