refactor(source): @antv/l7-source 渐进式重构(阶段 0-6,happy path 零行为变化) - #2882
Merged
Conversation
…port (stage 0.1) - 将 source/interface.ts 与 @antv/l7-core 重复的 7 个类型改为 re-export (DataType/IDictionary/IFeatureKey/IJsonData/IJsonItem/IParseDataItem/IParserData) - 保留 source 专有类型原地定义(栅格波段运算、瓦片数据源等) - 新增 docs/refactoring/source/ 存档:PLAN/PROGRESS/BACKLOG/README - 阶段 0.1: 纯 type-only 变更, 运行时无影响, 27/27 单测通过 详见 docs/refactoring/source/PROGRESS.md
- base-source.ts: excuteParser -> executeParser (private) - base-source.ts: caculClusterExtent -> calcClusterExtent (private) - factory.ts: registerTransform param transFunction -> transformFn (avoid shadowing existing type alias 'transformFunction') 私有方法/局部参数重命名, 对外 API 无影响, 27/27 单测通过。
- 新增 src/transform/types.ts: StatMethod + 6 个 cfg interface - filter/map/join/grid/hexagon: 内部 narrow 到具体 cfg 获得类型推导 - 函数签名保持 ITransform 兼容, as 断言无运行时代码, 行为不变 - 27/27 单测通过, tsc source/src 自身 0 错误
… (stage 0.4) - parser/raster/rgb.ts -> parser/rgb.ts, 函数名 rasterRgb -> rgb - parser/raster/ndi.ts -> parser/ndi.ts, 函数名 rasterRgb -> ndi - parser/rasterRgb.ts (注册名 rasterRgb) 保持不变, 与 rgb/ndi 功能各异 - 三个 parser 非重复代码, 本次仅命名/位置整理 - 顺带回填阶段 0.3 commit SHA (a41999c) 到 PROGRESS git mv 保留历史, default export 函数名改动对导入方无影响, 27/27 单测通过, tsc source/src 自身 0 错误。
调研发现 testTile parser 是 TileDebugLayer 调试图层的合法默认 parser (TileDebugLayer.ts / tile/utils/utils.ts / DebugTile.ts 三处下游依赖), 非 dev/demo 死代码。PLAN 阶段 0.5 前提有误, 标记 wontfix。 同步 PLAN 表格: 0.1-0.4 标 ☑ 0.5 标 ✗。
…stage 0.6) 重命名 src/source/ -> src/tile-source/(实为「瓦片数据源」),两个同名 VectorSource class 拆为语义清晰的名字: - source/vector.ts -> tile-source/mvt.ts, class -> MVTSource - source/geojsonvt.ts -> tile-source/geojsonvt.ts, class -> GeoJSONVTTileSource - source/index.ts -> tile-source/index.ts(兼容别名导出) - git rm source/baseSource.ts(抽象类死代码, 无继承者) - tile-source/index.ts 导出 MVTSource/GeoJSONVTTileSource, 保留 VectorSource 作 @deprecated 兼容别名 (= MVTSource) - 3 个 parser (mvt/geojsonvt/jsonTile) 更新 import 路径与类名 兼容性: layers 包 import { VectorSource } 经兼容别名零改动继续可用; VectorSource 正式移除留待阶段 7。 验证: source tsc 0 错 (基线 31 不变), layers tsc 229 pre-existing 无新增, prettier 通过, jest 27/27 通过。
PROGRESS.md 阶段 0.6 记录的 commit SHA 回填。
…tage 1.1)
从 base-source.ts God Class 抽出 cluster 状态机到独立
ClusterManager delegate, 对外 API 完全等价 (ISource 不动)。
- 新增 cluster-manager.ts (138 行): 封装 Supercluster 索引 +
clusterOptions + init/updateData/getClusters*/calcExtent
- base-source.ts (358 -> 314, -44):
- cluster/clusterOptions 字段 -> accessor 转发 delegate
- 删 clusterIndex 字段, getClusters*/updateClusterData 改转发
- initCluster/calcClusterExtent 删, processData 改调
clusterManager.init
- destroy 改调 clusterManager.destroy
构造期 new ClusterManager 注入 extent/invalidExtent getter 闭包,
delegate 不拥有 extent 状态 (留待 1.4 抽 Bounds)。
未启用 cluster 时 getClusters/updateClusterData 仍抛 TypeError,
与原行为一致 (DataSourcePlugin 契约前提)。
验证: source tsc 0 错 (基线 31 不变), layers tsc 229 pre-existing
无新增, eslint/prettier 通过, jest 27/27 通过 (cluster case
覆盖 updateClusterData 路径)。
PROGRESS.md 阶段 1.1 记录的 commit SHA 回填。
…tage 1.2) 从 base-source.ts 抽出瓦片管理职责到独立 TilesetAdapter delegate, 对外 API 完全等价 (ISource 不动)。 - 新增 tileset-adapter.ts (88 行): 封装 TilesetManager 实例的 创建/更新/销毁 + 7 个 reload/getTile 转发方法 - base-source.ts (314 -> 306, -8): - tileset/isTile 字段 -> getter 转发 adapter - 删 initTileset, executeParser 改调 tilesetAdapter.init - 7 个 reload*/getTile* 方法实现体改为转发 adapter - destroy 改调 tilesetAdapter.destroy 关键: adapter.manager 必须 public, 因 layers/tile/core/BaseLayer 直接读 source.tileset as TilesetManager 后自由操作实例。getter 转发让 layers 拿到同一个 TilesetManager 实例, 行为等价。 验证: source tsc 0 错 (基线 31 不变), layers tsc 229 pre-existing 无新增, eslint/prettier 通过, source jest 27/27, source+layers jest 81 passed/1 skipped/0 failed。
…ge 1.3)
从 base-source.ts 抽出 feature 查询/更新职责到独立
FeatureIndex delegate, 对外 API 完全等价 (ISource 不动)。
- 新增 feature-index.ts (115 行): 封装 getFeatureById /
getFeatureId / updateFeaturePropertiesById + dataArrayChanged
- 构造期注入 5 个 getter 闭包延迟读 Source 状态
- dataArrayChanged private 自持, setData 调 reset()
- base-source.ts (306 -> 290, -16):
- 删 dataArrayChanged 字段
- 3 方法体收敛为转发, updateFeaturePropertiesById
保留 emit('update')
- setData dataArrayChanged=false -> featureIndex.reset()
设计: emit 留 Source 转发端, delegate 不持 EventEmitter;
cloneDeep 与 'null' 越界占位保留原行为。
验证: source tsc 0 错 (基线 31 不变), layers tsc 229
pre-existing 无新增, eslint/prettier 通过, source jest 27/27。
… 1.4) 从 base-source.ts 抽出 extent/center/invalidExtent + setCenter 到 独立 Bounds value object, 对外 API 完全等价. - 新增 src/bounds.ts (50 行): extent/center/invalidExtent 三态 + update(bbox) 原子写入, 合并原 executeParser 末尾三行 + setCenter - base-source.ts (290 -> 292): 删 3 字段 + setCenter 方法, 加 bounds 字段 + 3 getter 转发; executeParser 三行 -> bounds.update(...); ClusterManager 闭包改读 this.bounds.extent/.invalidExtent 行数微增因新增 3 getter + import 但状态写入从 3 处分散收敛为 1 处. 验证: source tsc 31 基线不变, layers tsc 229 不变, eslint/prettier 通过, source jest 27/27 通过.
PROGRESS.md 阶段 1.4 记录回填实际 commit SHA.
… (stage 2.1) interface.ts: 新增 Parser<TData,TCfg,TResult> 统一契约 + KnownParsers 13 parser 映射 + KnownParserType 键名联合. 去重 RasterDataType / IRGBParseCfg (rgb.ts + ndi.ts 两份相同) 与 IImageCfg (image.ts) 收敛到 interface.ts 单一来源. factory.ts: ParserFunction = Parser 替代字面量 (类型擦除版, align cfg? optional). 13 个 parser 文件无需改签名 (隔离 TS 严格模式实测兼容). 对外 API 完全等价, 仅新增类型无运行时改动. 验证: source tsc 31 基线不变, layers tsc 229 不变, eslint/prettier 通过, source jest 27/27 通过. BALL_LOG: 闭环 BACKLOG RasterDataType/IRGBParseCfg 重复定义.
PROGRESS.md / BACKLOG.md 阶段 2.1 记录回填实际 commit SHA.
…leton (stage 2.2) PARSERS/TRANSFORMS 模块级可变对象抽到 ParserRegistry class + defaultRegistry 单例. factory.ts 4 函数保留为 defaultRegistry 薄转发 wrapper (加 @deprecated). 对包入口 re-export ParserRegistry 与 defaultRegistry, 对外 API 等价. 验证: source tsc 31 / layers tsc 229 / jest 27/27 / eslint+prettier 通过.
回填 PROGRESS.md 阶段 2.2 记录的 commit SHA c27b598.
… 2.3) 未注册改抛 ParserNotFoundError / TransformNotFoundError (替代 undefined 残报 TypeError). 签名保持 Parser / TransformFn 不带 | undefined, 调用方链式调用零变更. 新增 __tests__/parser-registry.spec.ts (10 tests) 覆盖抛错 + 内置注册. 验证: source tsc 31 / layers tsc 229 / jest 37/37 / eslint+prettier 通过.
回填 PROGRESS.md 阶段 2.3 记录与 BACKLOG 新增项的 commit SHA 6ffdcf5.
…itelist (stage 2.4)
index.ts 顶层 13 registerParser + 6 registerTransform 收敛到 builtins.ts 的 registerBuiltins().
package.json sideEffects 收紧为 [./es/index.js] 白名单 (沿用 packages/layers 约定).
默认 import { Source } 仍自动注册全 13 内置 parser, 运行期 0 行为变化.
验证: source tsc 31 / layers tsc 229 / l7 tsc 346 / jest 40/40 / eslint+prettier 通过.
回填 PROGRESS.md 阶段 2.4 记录与 BACKLOG 新增项的 commit SHA dd39acd.
…e 2.5) add createSource(data, cfg, registry?) factory wrapping new Source Source ctor gains optional registry 3rd param (default defaultRegistry) base-source/cluster-manager use this.registry.getParser/getTransform full registry-injection loop: parser execute + cluster re-parse index.ts re-exports createSource; factory.ts deprecated wrappers kept 5 new tests; tsc source 31 / jest 45 / eslint / prettier all green
回填 PROGRESS.md 阶段 2.5 记录的 commit SHA 占位符
…3.1.1)
new TileLoader interface (loadTile -> Promise<ITileSource | undefined>)
JsonTileLoader class: mechanical lift of getVectorTile closure body
jsonTile.ts (83->30) delegates getTileData to loader.loadTile
interface return type undefined-ified to support mvt resolve(undefined)
6 new loader unit tests via jest.mock('@antv/l7-utils') (first tile-loader net)
source tsc 31 / layers 229 / jest 51 / eslint / prettier all green
回填 PROGRESS.md 阶段 3.1.1 记录的 commit SHA 占位符
把 parser/mvt.ts 的模块级 getVectorTile 闭包机械抽取为实现 TileLoader
接口的 MVTLoader 类,行为与迁移前 100% 等价:
- 新增 src/loader/mvt-loader.ts (72 行): MVTLoader implements TileLoader
- URL 模板用 tileParams (TileLoadParams) 插值 [mvt 与 jsonTile 核心差异]
- getCustomData 入参 + MVTSource 构造用 tile.x/y/z
- 失败 (err/无数据) 统一 resolve(undefined), err 永不 reject
- 仅 getArrayBuffer 分支同步设 tile.xhrCancel = () => xhr.cancel()
(赋值先于异步回调, 取消语义等价)
- 重写 src/parser/mvt.ts (73 → 53 行): 薄包装委托 MVTLoader
- 保留 DEFAULT_CONFIG (mvt 原有, 与 jsonTile 不同)
- 删死导出 export type MapboxVectorTile (BACKLOG 阶段 0.4 项部分闭环)
- 新增 __tests__/loader/mvt-loader.spec.ts (155 行 / 6 tests)
- 双层 mock: jest.mock('@antv/l7-utils') + jest.mock MVTSource 构造器
- tileParams={x:10,y:20,z:30} vs tile={x:1,2,3} 精确锁死
[URL 用 tileParams / getCustomData+MVTSource 用 tile.xyz] 差异
- 覆盖 getArrayBuffer 成功/失败/空 + getCustomData 成功/失败
+ xhrCancel 设/未设 + requestParameters 透传
验证:
- tsc source 31 错基线不变 (全 core .glsl 噪音, 0 非 glsl)
- tsc layers 229 基线不变
- eslint / prettier 通过
- jest source: 9 suites / 57 tests 全通过 (旧 51 + 新 6)
- jest source + layers: 111 passed / 1 skipped / 0 failed
文档: PLAN.md 标 3.1.2 done; PROGRESS.md 追加记录 + 下一步 → 3.1.3
GeoJSONVTLoader; BACKLOG.md 闭环 mvt.ts 死 export + 更新 loader 单测状态
遗留: → 阶段 3.1.3 GeoJSONVTLoader (内存切瓦片, 无 xhrCancel, URL 用
tile.x/y/z); 阶段 3.2 RasterTileLoader; 阶段 3.3 image.ts 去 fetch
把 parser/geojsonvt.ts 的 getVectorTile 闭包 + 4 个投影助手机械抽取为 实现 TileLoader 接口的 GeoJSONVTLoader 类, 行为与迁移前 100% 等价: - 新增 src/loader/geojsonvt-loader.ts (199 行): GeoJSONVTLoader implements TileLoader - 构造期 geojsonvt(data, options) 一次性建内存空间索引 tileIndex - loadTile 走 tileIndex.getTile(tile.z/x/y) 同步切瓦片 (用 tile 索引查表) - GetGeoJSON(extent, tileParams.x/y/z, ...) 投影回 GeoJSON (用 tileParams!) - GeoJSONVTTileSource(vectorTile, tile.x/y/z) 用 tile 构造 Source - 无 getCustomData / 无 tile.xhrCancel / 始终 resolve ITileSource (空瓦片=[]) - 保留 // @ts-ignore (GetGeoJSON feature 含 relativeOrigin/coord 额外字段) - 新增 4 个投影助手下沉 loader: VectorTileFeatureTypes/signedArea/ classifyRings/GetGeoJSON (仅服务 loadTile, parser 不再需要) - 重写 src/parser/geojsonvt.ts (216 → 75 行, -141 行): 薄包装委托 loader - 保留 getOption (默认 options 合并, parser config 形状组装) - 保留 DEFAULT_CONFIG 不含 warp (geojsonvt 原本无, 与 mvt 不同) - 新增 __tests__/loader/geojsonvt-loader.spec.ts (132 行 / 6 tests) - 双层 mock: jest.mock('geojson-vt') + jest.mock GeoJSONVTTileSource - GetGeoJSON 真实运行, LineString feature 验证投影用 tileParams (tileParams.x=10 → lng=-67.5; 若误用 tile.x=1 → -135, 明显可区分) - TS 修复: geojson-vt export= 合并函数+namespace, (geojsonvt as jest.Mock) 报 TS2352, 改用 as unknown as jest.Mock 两步转换 (6 处统一别名)⚠️ 关键: geojsonvt 是 tile/tileParams 混用的第三种形态 (与 mvt/jsonTile 都不同): 索引查表用 tile / 坐标投影用 tileParams / Source 构造用 tile. spec 用 tileParams={x:10,y:20,z:5} vs tile={x:1,2,3} 精确锁死此差异, 不可简化. 验证: - tsc source 31 错基线不变 / tsc layers 229 基线不变 - eslint --max-warnings 0: 3 文件 0 错 0 警 - prettier --check: 通过 - jest source: 10 suites / 63 tests 全通过 (旧 57 + 新 6) - jest source + layers: 117 passed / 1 skipped / 0 failed 文档: PLAN.md 标 3.1.3 done (阶段 3.1 收尾); PROGRESS.md 追加记录 + 下一步 → 3.2 RasterTileLoader; BACKLOG.md 更新 loader 单测状态 (阶段 3.1 三个瓦片 矢量 loader 全覆盖, 剩 raster-tile/image) 遗留: → 阶段 3.2 RasterTileLoader 大 switch 拆分 (建议先抽分发器再拆 6 小 loader 两步渐进); 阶段 3.3 image.ts 去 fetch. 阶段 3.1 (Parser/Loader 解耦 — 瓦片矢量部分) 全部完成
ISource + Source 新增单调递增 dataVersion 计数器(零行为变化)。 bump 点: - setData(全量 reseat,reseat 同步阶段 bump,先于 update fire) - updateFeaturePropertiesById(原地属性变更,emit 前同步 bump) 不 bump: - updateClusterData(zoom 驱动聚合视图重算,originData 未变,属派生视图) - 构造期首次 parse(generation 0 = 初始数据) 为何不含 4.3b 行为切片:setData 为运行时热路径,改错即回归。 4.3a 先铺版本号 infra(同 4.1 infra→4.2 消费模式),4.3b 单切片 在版本号基础上做同 schema skip re-parse,需先补调用链 / 副作用 / spec。 契约安全:implements ISource 仅 Source 一处,无 subclass,新增 required 字段无破坏;tsc layers 229 baseline 不变。 spec: data-version.spec.ts 5 case。 基线: tsc core 0 / source 0(去glsl) / layers 229 / jest source 107(+5) / layers-plugins 6 / eslint 0。
setData 的 init().then(emit 'update') 旧路径无 .catch:re-parse 失败时 'update' 不 fire(消费方 hang)+ 未捕获 rejection(吞错,同 4.2 为构造期 initPromise 修的 swallow/hang 模式)。现加 .catch(emit 'error'): - 成功路径字节级不变,'update' 契约不变 - 失败由 'error' 事件 surface(eventemitter3 无 Node 抛错语义,无监听即 静默,安全) - 零签名变化(void→void),零调用方影响 原 4.3b「同 schema skip re-parse」经勘探判定 dead-end:parse / tilesetAdapter.init / bounds.update / clusterManager.init / executeTrans 全 data-dependent,setData 本质即换 data,skip 无收益。结案记 BACKLOG。 spec: set-data.spec.ts 3 case(成功基线锁 + 失败 surfacing + 无 unhandled rejection)。 基线: tsc core 0 / source 0(去glsl) / layers 229 / jest source 110(+3) / layers-plugins 6 / eslint 0。
勘探 new Source / Source.create / createSource 全仓 call sites: - 生产 new Source( 仅 1 处:DataSourcePlugin.ts:15,正是 4.2 合法的 new Source + await source.ready race-free 模式(非 Source.create) - Source.create( / createSource( 生产零消费(仅 spec + 包内定义/doc) - source 包内其余 new Source( 命中均为工厂内部 wontfix 五条理由: 1. 自相矛盾 — warn 会 nag 唯一合法生产消费方 2. 零 bad-pattern call site(4.2 已清掉唯一真实 race) 3. Source.create/createSource 生产零采用 → deprecation 无对象 4. new Source 是公开构造器,deprecate 属 major 不该 minor 推 5. race 已由 4.1 ready getter 在消费侧解决 纯评估切片无代码改动(同 4.2 后续 f20b3e6 模式)。 阶段 4 主题全部收敛,后续转 stage 3.2.2 / stage 5。 PROGRESS 下一步重指向 3.2.2 loader 解耦弧延续; PROGRESS/PLAN/BACKLOG 同步标注 wontfix。
6 分支 switch(IMAGE/ARRAYBUFFER/CUSTOMIMAGE/CUSTOMTERRAINRGB/
CUSTOMARRAYBUFFER/CUSTOMRGB)拆成 4 独立小 loader(PLAN 偏差:6 enum
仅 4 种取数行为,CUSTOMIMAGE/CUSTOMTERRAINRGB 共享、CUSTOMARRAYBUFFER/
CUSTOMRGB 共享,拆 6 成对重复故拆 4)+ 引入 IRasterTileLoader 接口,
分发器持 Map<RasterTileType, IRasterTileLoader> 按 tileDataType 选 loader、
未命中走 ImageRasterLoader 兜底(保 default 分支语义)。
新增 4 loader:raster/{image,buffer,custom-image,custom}-raster-loader.ts。
重构 raster-tile-loader.ts:删 loadCustomImageData/loadCustomRasterData
私有方法(逻辑移入对应小 loader),唯一消费方 parser/raster-tile.ts 零改动。
patch 级纯内部重构,公开 API(ctor 3 参 + loadTile(tileParams,tile))不变。
验证:prettier/eslint/tsc source 0/tsc layers 229 不变/jest source 110
(raster-tile-loader.spec 16 case 全过=行为等价证明,零新增 spec)。
两处交接后修复:custom-image loader 注释残留 fewer-params 措辞(截断句)
已清;import type→import 值导入(CustomDataProvider 作 new 值用,TS1361)。
阶段 3 主题(3.1/3.2/3.3/3.4)全部收敛。
纯评估切片,无代码改动(同 4.1b 模式)。勘探依赖图 + call sites 判定
5.1 原 PLAN「迁 layers/utils + source type re-export 过渡」可行性。
关键发现:core→utils / source→{core,utils} / layers→{core,source,utils}。
- Approach A(target=layers)❌:source→layers re-export 循环 →
type re-export 过渡不可能 → source 公开导出须彻底移除 = breaking
(major 级),与「major removal 留未来」纪律冲突。
- Approach B(target=utils,推荐)✓:解耦 IParseDataItem 类型依赖
(函数仅用 item.coordinates,改 minimal interface),source
re-export 过渡保公开 API(minor-safe),BaseLayer 改 import utils。
唯一消费方 BaseLayer.ts:42;examples/dev/docs 零代码 import。
PLAN 5.1/5.2 已据发现修订;BACKLOG 记 Approach B 类型设计待决。
下一「继续」执行 Approach B。
…utils
relative-coordinates.ts 从 @antv/l7-source 迁到 @antv/l7-utils(包边界
修复,Approach B)。解耦 IParseDataItem 类型依赖:4 函数 +
IRelativeCoordinateResult 改泛型 <T extends { coordinates?: any[] }>,
T 透传到返回类型,消费方 BaseLayer 传 IParseDataItem[] 得
result.dataArray: IParseDataItem[],赋回 data.dataArray 零 type 涟漪。
- 新增 packages/utils/src/relative-coordinates.ts(泛型,无 core 依赖)
- utils index 加 export* from './relative-coordinates'
- source index: 删本地 export,改命名 re-export 自 @antv/l7-utils
(transitional 保公开 API,minor-safe)
- 删 packages/source/src/utils/relative-coordinates.ts(git rename)
- BaseLayer.ts:42 import 自 l7-source 合并入既有 l7-utils import
类型设计最终 = 泛型(非 scoping 草拟 minimal interface):BaseLayer:1423
把 result.dataArray 赋回 IParseDataItem[] 字段,minimal interface 缺
_id 会 tsc 失败,故泛型保 T=IParseDataItem 零涟漪。
验证:prettier/eslint/tsc utils 0/tsc source 0(glsl)/tsc layers
229 不变/jest source 110/jest layers 57 passed 1 skipped = 改前
stash 对比 baseline 完全一致(严格无回归)。阶段 5 收尾。
filter/map/join 三处「原地改 data.dataArray + 返回同引用」改为返回
新对象 { ...data, dataArray: ... },不再改入参,利于缓存/diff。
零行为变化(executeTrans 等价性证明):base-source executeTrans 对每个
transform 执行 Object.assign(this.data, getTransform(type)(this.data, tran))。
改前原地改 dataArray + 返回同引用,Object.assign(this.data, this.data) = no-op
(dataArray 已就地改)。改后返回 B = {...A, dataArray:newArr}(A=this.data 未改),
Object.assign(A, B) 回写:A.dataArray=newArr、其余 props B[p]===A[p] 故 no-op →
逐字段等价,且 this.data 引用稳定(Object.assign 不换引用)。callback 执行期间
data.dataArray(RHS 读取)两种实现下均为原始数组,闭包读亦等价。
切片边界(PLAN line 100 明确 filter/map/join):grid/hexagon 本就返回新对象
(已不可变,无需改);cluster transform 已 @deprecated,cluster() no-pointIndex
分支返回 Supercluster(ClusterManager 直调 Path A),pointIndex 分支 mutation
属 deprecated/legacy dead branch → defer BACKLOG。不动 executeTrans、builtins
注册、types cfg 契约。
验证 6 项全过(回归网=5 transform spec):prettier/eslint 0/tsc source 0
(去 glsl)/tsc layers 229(baseline)/jest source 110(=baseline,filter/grid/
hexagon/map/join 5 case 全过)/jest layers 57 passed 1 skipped(=baseline)。
遗留:cluster pointIndex 分支不可变 + executeTrans 直装 + 6.2 transform
单测,记 BACKLOG。PROGRESS SHA 占位「commit 待补」后续 backfill。
6.2 的真实缺口是 raster 家族 4 个纯函数 parser(同步 band 操作 + 坐标投影, 无需 mock loader)。image/mvt/geojsonvt/jsonTile/raster-tile 已由阶段 3 loader spec 覆盖 happy+error(CustomDataProvider 6 case)。 新 spec: - parser/raster.spec.ts 10 case:raster 7(number[] 直传/默认 extent 4 角 派生/自定义 extent/自定义 coordinates 非矩形/format 提供 isNumberArray 优先走直传/ArrayBuffer 路径 bandsOperation 产 Promise 进 data 异步契约/ ArrayBuffer[] 包装同路径)+ rasterRgb 3(直传+rest 透传) - parser/rgb.spec.ts 9 case:rgb 5(显式 R/G/BMinMax 交错输出/负值钳 0/ 自定义 bands 通道序/缺 MinMax 走 percentile/不足 3 波段 warn-only 无 guard 致崩)+ ndi 4(归一化差值/自定义 bands/rest 透传/不足 2 波段致崩) 既存隐患记 BACKLOG(6.2 补测试不改行为):rgb extent 无默认值缺校验 + rgb/ndi 波段不足仅 warn 不 guard 致崩。 验证 4 项全过:prettier/eslint 0/tsc source 0(去 glsl)/jest source 126 (110 baseline + 16 新增, 19 suites 全绿)。PROGRESS SHA 占位后续 backfill。
cluster 110/217、grid 2511、hexagon 1934 这类「具体大数」断言随 Supercluster 算法 / 投影精度 / 测试数据增删即碎,却无真实行为回归 → 改为下界 + 结构形状 断言,留足算法/数据漂移余量,同时仍能捕获聚合完全失效(返 0/undefined)真回归。 改造(4 spec 零生产代码改动): - source.spec: cluster 110/217 → zoom2 下界>50+coordinates + zoom3>zoom2 单调 (簇拆分);grid 2511 → >1000+coordinates/count/_id:number;hexagon 1934 → 同型 - data-version.spec: cluster 110/217 → >50(锁的是 dataVersion===0 不 bump 契约, 点数非主角) - create-async.spec: 两处 110 → >50(锁 async 工厂/自定义 registry 端到端) - create-source.spec: 两处 110 → >50(锁 sync 工厂/自定义 registry 端到端) 保留:纯数 parser 小整数 + 值断言(value===100 等非脆弱)+ 到Equal( Point.features.length-1)(filter 已具相对语义)。 验证 4 项零回归:prettier/eslint 0/tsc source 0(去 glsl)/jest source 126 (=6.2 baseline)/tsc layers 229(baseline)/jest layers 57 passed 1 skipped (baseline)。PROGRESS SHA 占位后续 backfill。
…spec
阶段 6.4(minor-safe 新增 API,零行为变化):
- 新类型 ISourceStats(interface.ts,7 字段只读快照):rows / bbox /
parserType / tileCount / isTile / cluster / dataVersion;BBox 加进 turf
import;经 export * from './interface' 由 index 透出。
- 新方法 Source.stats()(base-source.ts,置于 getParserType 之后):纯只读,
不变 Source 状态。rows 用 data?.dataArray?.length ?? 0 兜底;tileCount 用
tileset?.currentTiles.length ?? 0;parserType 用 (parser as IParserCfg).
type || 'geojson' 与 executeParser 默认逻辑一致。对 new Source /
Source.create / setData / updateFeaturePropertiesById 路径零行为变化
(未触及 ISource 核心契约,未加 deprecation)。
- 新 spec source-stats.spec.ts(7 case):非瓦片 geojson 全字段快照;
parserType 与 getParserType 一致;聚合源 cluster=true 初始未 zoom rows
=全量 feature;瓦片源 mvt isTile=true/parserType=mvt/tileCount=0/rows=0;
setData 后 stats 反映新 rows + dataVersion bump(once('update') 等 re-parse);
updateFeaturePropertiesById bump dataVersion 行数不变;stats 幂等不改状态。
验证(5 项全过,零回归):prettier ✓ / eslint 0 ✓ / tsc source 0(去 glsl)✓ /
jest source 133 passed(126 baseline + 7 新)✓ / tsc layers 229(baseline)✓ /
jest layers 57 passed 1 skipped(baseline)✓。
至此 PLAN 阶段 6 完整收敛(6.1/6.2/6.3/6.4 全 ☑),阶段 0-6 全部落地。
重构参考:docs/refactoring/source/PLAN.md › 阶段 6.4
🦋 Changeset detectedLatest commit: 38b58d5 The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Contributor
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
lzxue
added a commit
that referenced
this pull request
Jul 23, 2026
source.{zh,en}.md 新增「兼容性 / Compatibility」小节:明确旧 new Source(data, cfg) /
cluster: true / ISourceCFG 旧字段 / 'update' 事件全部向后兼容,新 API 为可选迁移路径。
cluster 段补弃用提示:transforms: [{ type: 'cluster' }] 已弃用,改用 cluster: true。
修正 en 文档既有 embed 引用 bug:source.en.md / mvt.en.md / raster_tile.en.md 原嵌入
method.zh.md(中文)→ 改为 method.en.md(英文版早已存在)。该 bug 非 本次重构引入
(commit 1d5c5fa),本轮顺手修正。
重构存档同步:README「当前状态」更新为阶段 0-6 已合并;PROGRESS 追加 PR #2882 合并 /
文档补全 / 兼容性核查三条记录。
lzxue
added a commit
that referenced
this pull request
Jul 23, 2026
master 的 @antv/l7-source 渐进式重构(#2882)与本分支搬运(source→layers)冲突,逐类解决: - layers/src/source 与 __tests__/source 完整采用 master 新结构 (tile-source/、parser/ndi.ts、base-source.ts、create-source.ts 等) - relative-coordinates 已由 master 上移至 @antv/l7-utils,保留 utils 版本 - maps 底图迁移:采用 master 的 BaseMap/MapboxBaseMap 新结构, @antv/l7-map 引用改 ../mapbase(map 包已并入 maps/src/mapbase) - BaseLayer: @antv/l7-source → ../source,保留 master 的 processRelativeCoordinates - 删除被合并包(source/component/map/renderer)残留,版本号同步 master 2.30.0-beta.0 - l7/layers/scene 顶部元数据冲突取 master 版本号;test-utils 保留 peerDependencies - __tests__/source 测试相对路径同步调整(../src → ../../src/source) 构建验证:layers(271) / maps(88) / scene(66) / l7(UMD 1.4M < 1.7Mb) 全部通过
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概述
对
@antv/l7-source包进行渐进式重构,阶段 0-6 全部落地。核心公开 happy path(new Source/setData成功路径)字节级零行为变化;另有 4 处 explicitly-minor 的错误/边界路径 strictly-better 改进(详见下「行为变化清单」,均为修 bug / 改善错误 surface,非回归)。坚持渐进式纪律:每阶段独立切片(代码 + 测试 + 文档 + commit + SHA 回填 + post-commit 基线核验),可单独审查 / revert。完整路线图、进度记录、遗留问题已存档:
docs/refactoring/source/PLAN.md(路线图 + 现状诊断)docs/refactoring/source/PROGRESS.md(倒序逐阶段记录 + commit SHA)docs/refactoring/source/BACKLOG.md(遗留问题:位置/问题/建议/状态)改动范围(阶段 0-6)
阶段 0 — 低风险清理(无行为变更)
source/interface.ts改为从@antv/l7-corere-export,删重复定义;MapboxVectorTile/IRGBParseCfg各定义一处excuteParser→executeParser、caculClusterExtent→calcClusterExtent、transFunction→transformFunctionIFilterCfg/IJoinCfg/IGridCfg/...)parser/rasterRgb.ts↔parser/raster/rgb.ts同名 function,统一为parser/rgb.ts、parser/ndi.tstestTile移出生产 entry — wontfix(TileDebugLayer 合法默认 parser)src/source/→src/tile-source/;两个VectorSource改名MVTSource/GeoJSONVTTileSource阶段 1 — 拆解 God Class(内部 delegate,对外透明)
ClusterManager:封装 Supercluster + clusterOptions + updateClusterData + getClusters*TilesetAdapter:包 initTileset + 7 个 reload*/getTile* 方法FeatureIndex:getFeatureById/getFeatureId/updateFeaturePropertiesByIdBoundsvalue object:extent/center/setCenter/invalidExtent阶段 2 — Parser 接口标准化 + 注册机制现代化
interface Parser<TData, TCfg, TResult extends IParserData>PARSERS/TRANSFORMS全局 Map →ParserRegistryclass;旧全局函数作 deprecation wrappergetParser(type)未注册抛ParserNotFoundError(替代旧 undefined 直接 call 的 TypeError)package.json设sideEffects白名单["./es/index.js"];副作用注册显式化为registerBuiltins()createSource(data, cfg, registry?)阶段 3 — Parser 与 Loader 解耦
TileLoader接口;getVectorTile闭包抽成MVTLoader/JsonTileLoader/GeoJSONVTLoaderRasterTileLoader分发器 + 4 个小 loader(IMAGE/ARRAYBUFFER/CustomImage/CustomRaster)image.ts抽ImageLoader类,parser 不再自 fetchgetCustomData/getCustomImageData统一到CustomDataProvider阶段 4 — 异步生命周期与状态
Source.create(data, cfg): Promise<Source>async 工厂 +readygetter,消除source.dataraceawait source.ready;ISource加readonly ready: Promise<void>契约dataVersion版本号计数器(setData / updateFeaturePropertiesById bump)setData失败 surfacing(swallow/hang →'error'事件)clusterOptions.enable死字段;合并 cluster 两条路径阶段 5 — 包边界修复
relative-coordinates.ts迁出@antv/l7-source到@antv/l7-utils(layer-concern 坐标变换不再由 source 包承担)阶段 6 — 测试 & 性能 & 不可变
filter/map/join返回新对象,不再原地改入参)Source.stats()只读快照(ISourceStats类型 + 7 case spec):rows / bbox / parserType / tileCount / isTile / cluster / dataVersion阶段 7(可选,长期,不在本 PR 范畴)
7.1 显式 class 层级 / 7.2 source pipeline / 7.3 geojsonvt-decoder 抽离 — 记于 PLAN 待后续 major 演进。
绝大多数改动真零行为变化(对外语义等价、字节级不变):阶段 0、1、3、4.1、4.3a、5、6 均属此类。以下 4 处为 explicitly-minor 的错误/边界路径改进,需要 release note 提及:
undefined直接 call →TypeError: ... is not a functionParserNotFoundError(type)(自定义 Error)DataSourcePlugininit 等 source 就绪source.on('update')手写 Promise,任意 update 类型即 premature resolve;init 失败静默 hangawait source.ready,init 完成才 resolve;init 失败 reject surfacesetData失败 surfacing.then(emit 'update')无 catch → 失败'update'不 fire(消费方 hang)+ unhandled rejection(吞错).catch(emit 'error'),失败由'error'事件 surface(eventemitter3 无 Node 抛错语义)cluster: boolean与transforms:[{type:'cluster'}]两条路径并存;后者 broken(cluster()返 Supercluster 被executeTrans的Object.assign腐蚀 source.data,全仓零使用);clusterOptions.enable死字段clustertransform 改clusterTransformdeprecation wrapper(warn once + delegate);删enable死字段;ClusterManager.init仍走cluster()零 warninghappy path 零行为变化指的是:
new Source(data, cfg)正常解析、setData成功 re-parse、Source.create/createSource、cluster 正常聚合、瓦片 source 正常——这些公开成功路径的输出、事件时序、数据形状字节级不变。上面 4 处都只在「错误/边界/已废弃」情形触发。验证(5 项全过,零回归)
文档 / 后续
docs/refactoring/source/PROGRESS.mddocs/refactoring/source/BACKLOG.md(含 6.2 发现的 rgb/ndi 无 guard 隐患、4.3b stale-data recovery、阶段 4.5 移除 cluster transform 注册需 major changeset 等)Review 提示
本 PR 含 67 个 commit,每阶段独立切片。建议按 PLAN 阶段顺序审查,每个阶段内代码 + 测试 + 文档自洽,可独立 revert。重点请关注「行为变化清单」中 4 处 minor 变更是否需要发版说明 / changeset。
🤖 Generated with Claude Code