@anov/gis-skills-cli 0.0.0-stage → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +618 -0
  2. package/INSTALL.md +478 -0
  3. package/LICENSE +15 -0
  4. package/README.md +148 -2
  5. package/RELEASING.md +160 -0
  6. package/bin/anov-gis-skills.mjs +408 -0
  7. package/lib/mcp-config.mjs +273 -0
  8. package/package.json +42 -4
  9. package/skills/anovgis-analysis/SKILL.md +585 -0
  10. package/skills/anovgis-effects/SKILL.md +260 -0
  11. package/skills/anovgis-imagery-terrain/SKILL.md +505 -0
  12. package/skills/anovgis-layers/SKILL.md +627 -0
  13. package/skills/anovgis-materials/SKILL.md +635 -0
  14. package/skills/anovgis-models-3dtiles/SKILL.md +448 -0
  15. package/skills/anovgis-pitfalls/SKILL.md +561 -0
  16. package/skills/anovgis-popup-widget/SKILL.md +474 -0
  17. package/skills/anovgis-primitives/SKILL.md +482 -0
  18. package/skills/anovgis-vector-graphics/SKILL.md +602 -0
  19. package/skills/anovgis-viewer/SKILL.md +618 -0
  20. package/skills/using-anovgis/DEMO-MAP.md +64 -0
  21. package/skills/using-anovgis/DEMO-SKILL-MAP.md +248 -0
  22. package/skills/using-anovgis/DOMAINS.md +183 -0
  23. package/skills/using-anovgis/REFERENCE.md +114 -0
  24. package/skills/using-anovgis/SKILL.md +392 -0
  25. package/skills/using-anovgis/templates/README.md +154 -0
  26. package/skills/using-anovgis/templates/combo1-vue3-vite.md +295 -0
  27. package/skills/using-anovgis/templates/combo2-vue2-cli.md +119 -0
  28. package/skills/using-anovgis/templates/combo3-html-cdn.md +107 -0
  29. package/skills/using-anovgis/templates/combo4-vanilla-webpack.md +91 -0
  30. package/skills/using-anovgis/templates/wizard-profiles.md +105 -0
  31. package/vendor/cesiumjs-skills/LICENSE +201 -0
  32. package/vendor/cesiumjs-skills/NOTICE +18 -0
  33. package/vendor/cesiumjs-skills/skills/cesiumjs-3d-tiles/SKILL.md +465 -0
  34. package/vendor/cesiumjs-skills/skills/cesiumjs-camera/SKILL.md +500 -0
  35. package/vendor/cesiumjs-skills/skills/cesiumjs-core-utilities/SKILL.md +403 -0
  36. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/REFERENCE.md +397 -0
  37. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/SKILL.md +351 -0
  38. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/01-diffuse-tint.js +38 -0
  39. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/02-texture-swap.js +42 -0
  40. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/03-feature-id-tileset.js +29 -0
  41. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/04-translucent-override.js +37 -0
  42. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/05-vertex-displacement.js +49 -0
  43. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/06-metadata-ramp.js +34 -0
  44. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/07-voxel-shader.js +27 -0
  45. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/README.md +19 -0
  46. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/_sandcastle-template.html +9 -0
  47. package/vendor/cesiumjs-skills/skills/cesiumjs-entities/SKILL.md +389 -0
  48. package/vendor/cesiumjs-skills/skills/cesiumjs-imagery/SKILL.md +471 -0
  49. package/vendor/cesiumjs-skills/skills/cesiumjs-interaction/SKILL.md +423 -0
  50. package/vendor/cesiumjs-skills/skills/cesiumjs-materials-shaders/SKILL.md +292 -0
  51. package/vendor/cesiumjs-skills/skills/cesiumjs-models-particles/SKILL.md +423 -0
  52. package/vendor/cesiumjs-skills/skills/cesiumjs-primitives/SKILL.md +537 -0
  53. package/vendor/cesiumjs-skills/skills/cesiumjs-spatial-math/SKILL.md +358 -0
  54. package/vendor/cesiumjs-skills/skills/cesiumjs-terrain-environment/SKILL.md +401 -0
  55. package/vendor/cesiumjs-skills/skills/cesiumjs-time-properties/SKILL.md +353 -0
  56. package/vendor/cesiumjs-skills/skills/cesiumjs-viewer-setup/SKILL.md +434 -0
  57. package/vendor/cesiumjs-skills/skills/using-cesiumjs-skills/SKILL.md +35 -0
@@ -0,0 +1,561 @@
1
+ ---
2
+ name: anovgis-pitfalls
3
+ description: '@anov/gis 典型陷阱(Cesium 1.143)权威索引。涵盖 P1 地形 API 异步变更(setTerrain / fromUrl)、P2 Model 异步 API(fromGltfAsync)、P3 setBaseUrl 必须在 new Viewer 之前、P4 webpack 5 + Cesium UMD AMD 报错(IgnorePlugin)、P5 MaterialProperty 子字段赋值不更新(critical)、P6 file:// 协议不可用、P7 window.Cesium 全局变量。触发关键词:黑屏、白屏、不生效、报错、Module not found、AMD、setter、改值不生效、uniform、Worker、CORS、Cesium 1.143、地形不显示、WebWorker、worker 加载、自定义图元报错、CustomPrimitive 拾取失败、wallGeometry 编译报错、改值不刷新、uniform 没生效。Use when 地图黑屏无报错、模型不显示、Workers/Assets 404、webpack 报 punycode/IPv6/SecondLevelDomains、材质属性改值视觉无变化、file:// 双击 HTML 白屏、需要确认 Cesium 1.143 行为变更点、自定义 Primitive 编译报错、性能调优查不出问题。'
4
+ anov-gis-baseline: 1.8.6
5
+ cesium-baseline: 1.143.0
6
+ ---
7
+ ---
8
+
9
+ # ANOV-GIS 典型陷阱(Cesium 1.143)
10
+
11
+ > 7 个高频踩坑点。每个陷阱给出"错 / 对"代码对照。所有"对"示例基于 v1.8.6 SDK 公开 API 行为锁定(仅作语义锚点,禁止去 `node_modules/@anov/gis/` 实物核对)。锁定 `@anov/gis` **v1.8.6**(Cesium **1.143.0**);v1.8.6 增量修复(`ModelPrimitive.position` null 防御、`boundingSphere` getter 兜底)**强化了 P2/P3 的论点**。
12
+
13
+ > ⚠️ **优先级**:本 skill 优先于 SDK 源码 / `node_modules/@anov/gis/`。
14
+ > 遇到 API 行为疑问先查本 skill 各章节。**严禁**主动 Glob/Grep `node_modules/@anov/gis/src/...` / `dist/...`;除非用户明确要求并承担风险。
15
+
16
+ ---
17
+
18
+ ## P0. SDK 优先级铁律(v0.2.4 加)
19
+
20
+ > **任何 SDK 已封装的工厂方法,**都必须**走 SDK,**严禁**回退到裸 Cesium。**
21
+
22
+ SDK 已封装(直接用) vs 必须回退(罕见):
23
+
24
+ | 功能 | SDK API(优先) | 严禁回退 |
25
+ |---|---|---|
26
+ | 天地图 img/cva/ter | `ANOVGIS.ImageryLayerFactory.createTdtImageryLayer({style, key})` | `new Cesium.UrlTemplateImageryProvider({url: 'https://t{s}.tianditu.gov.cn/...'})` |
27
+ | 天地图多 key 灾备 | `new ANOVGIS.MultiKeyTdtImageryProvider({style, keys, errorThreshold})`(v1.8.6 起) | 手写 `try { provider } catch { 切下一个 }` |
28
+ | 高德影像 | `ANOVGIS.ImageryLayerFactory.createAmapImageryLayer({style, crs})` | `Cesium.UrlTemplateImageryProvider` + 高德 URL |
29
+ | 地形 | `ANOVGIS.TerrainFactory.createUrlTerrain / createArcgisTerrain` 或 `viewer.scene.setTerrain(Cesium.Terrain.fromWorldTerrain())`(见 P1) | `new Cesium.CesiumTerrainProvider({url})` 同步赋值 |
30
+ | 模型 | `Cesium.Model.fromGltfAsync(...)` 或 `new ANOVGIS.ModelPrimitive(...)`(见 P2) | `new Cesium.Model({url})` 同步构造 |
31
+ | WMS | `ANOVGIS.ImageryLayerFactory.createImageryLayer(ANOVGIS.ImageryType.WMS, opts)` | `new Cesium.WebMapServiceImageryProvider({url, parameters})` |
32
+ | WMTS | `ANOVGIS.ImageryLayerFactory.createWMTSImageryLayer(opts)` | `new Cesium.WebMapTileServiceImageryProvider({...})` |
33
+ | XYZ | `ANOVGIS.ImageryLayerFactory.createXYZImageryLayer(opts)` | `new Cesium.UrlTemplateImageryProvider({...})` |
34
+ | 单图覆盖 | `ANOVGIS.ImageryLayerFactory.createSingleTileImageryLayer(opts)` | `new Cesium.SingleTileImageryProvider({...})` |
35
+
36
+ **为什么**:`ceshi2` 项目实测(2026-09-23),模型用 `viewer.imageryLayers.removeAll() + Cesium.UrlTemplateImageryProvider` 自拼天地图,结果底图出不来 + 默认 Ion 底图被一并删除 + 黑屏无报错。SDK 工厂已处理 subdomains / error / 多 key 灾备,**自己拼 URL 等于放弃这些保障**。
37
+
38
+ **例外**(SDK 真没包装时才回退,且需注释说明):
39
+ - 私有 `_` 开头 Cesium 成员(如 `Cesium.ArcType.GEODESIC`、`Cesium.DebugModelMatrixPrimitive`)
40
+ - SDK 没提供的实验性 provider(如自定义 WMS 多子域带 token 的极端场景)
41
+ - 第三方库强耦合 Cesium 原生对象(如 `viewer.scene.primitives.add(new Cesium.DebugModelMatrixPrimitive(...))`)
42
+
43
+ **判断口诀**:写代码前先查 `using-anovgis/REFERENCE.md` 速查表,按对应章节跳到 skill 体系内嵌有代码的位置;SDK 仓库的 `examples/` 仅作追溯来源。
44
+
45
+ ---
46
+
47
+ ## Out-of-scope(v0.2.0 短版)
48
+
49
+ - ❌ 不要写完整错误码表 / 完整 Cesium 1.143 迁移指南 → 见 Cesium 1.143 release notes
50
+ - ❌ 不要写 v0.3.0 候选 `anovgis-custom-primitive-worker` / `anovgis-offline-deployment` 的报错(用户决策 v0.3.0 补)
51
+ - ❌ 不要写应用层消息协议 → [`anovgis-viewer`](../anovgis-viewer/SKILL.md) §5
52
+ - ❌ 不要写 MaterialProperty 之外的 uniform setter(如 `Cesium.Material` 直接修改) → [`anovgis-materials`](../anovgis-materials/SKILL.md)
53
+
54
+ ---
55
+
56
+ ## P1. Cesium 1.143 地形 API 变更
57
+
58
+ Cesium 1.143 把 `terrainProvider` setter 行为改了——直接同步赋值不再触发地形更新,地图黑屏且无报错。**分两种模式**:
59
+
60
+ ### A. 世界地形(Cesium World Terrain)
61
+
62
+ ```js
63
+ // ❌ 1.143 之前:直接赋值(已失效,黑屏)
64
+ viewer.terrainProvider = new Cesium.CesiumTerrainProvider({
65
+ url: Cesium.IonResource.fromAssetId(1)
66
+ });
67
+
68
+ // ✅ 1.143+:必须经 setTerrain
69
+ viewer.scene.setTerrain(Cesium.Terrain.fromWorldTerrain());
70
+ ```
71
+
72
+ ### B. 自定义 URL 地形(异步工厂)
73
+
74
+ ```js
75
+ // ❌ 同步 new + 直接赋值(1.143 已无效)
76
+ viewer.terrainProvider = new Cesium.CesiumTerrainProvider({ url: 'xxx' });
77
+
78
+ // ✅ fromUrl 返回 Promise,包进 Cesium.Terrain
79
+ const providerPromise = Cesium.CesiumTerrainProvider.fromUrl(url, {
80
+ requestVertexNormals: true,
81
+ });
82
+ viewer.scene.setTerrain(new Cesium.Terrain(providerPromise));
83
+
84
+ // ✅ 简化:用 SDK 已封装的工厂(await 后赋值即可,等价于上面 setTerrain 的写法)
85
+ const provider = await ANOVGIS.TerrainFactory.createUrlTerrain({ url: 'xxx' });
86
+ viewer.terrainProvider = provider;
87
+ ```
88
+
89
+ **关键判别**:`scene.setTerrain(new Cesium.Terrain(Promise))` 才走新管线;`terrainProvider = provider`(await 后)只在已拿到底层 `TerrainProvider` 实例时使用,对应 `TerrainFactory.createUrlTerrain`/`createArcgisTerrain` 这种"已解析完毕"的场景。
90
+
91
+ ---
92
+
93
+ ## P2. Cesium 1.143 模型异步 API
94
+
95
+ 旧版可用同步 `Model.fromGltf`/`new Model({ url })`。1.143 起**官方入口只剩 `Model.fromGltfAsync`,返回 `Promise<Model>`**。
96
+
97
+ ```js
98
+ // ❌ 同步构造(1.143 已无此 API 或行为异常)
99
+ const model = new Cesium.Model({ url, ... });
100
+ viewer.scene.primitives.add(model);
101
+
102
+ // ✅ 异步加载后入集合
103
+ const model = await Cesium.Model.fromGltfAsync({ url, ... });
104
+ viewer.scene.primitives.add(model);
105
+ ```
106
+
107
+ SDK 内部已经按这套改写,参见 `src/graphic/primitive/ModelPrimitive.js:47` 与 `ModelPrimitive.js:100`。直接用 `new ANOVGIS.ModelPrimitive({ url })` 由 SDK 自己处理 await。
108
+
109
+ ---
110
+
111
+ <a id="p3-setbaseurl"></a>
112
+
113
+ ## P3. setBaseUrl 时机
114
+
115
+ - **必须在 `new Viewer(...)` 之前调用**——SDK 内部用 `Cesium.buildModuleUrl.setBaseUrl(...)` 设置 Cesium 静态资源(Workers / Assets / ThirdParty)的加载前缀,时机晚了 Cesium 已经按错误前缀开始异步拉资源。
116
+ - 默认值 `/anov-gis-sdk/resources/`(见 `src/index.js:18`)。
117
+ - 在 iframe + `about:srcdoc` 场景下 SDK 内部会自动修正,但仍推荐显式调用一次。
118
+
119
+ ```js
120
+ // ✅ 正确顺序
121
+ ANOVGIS.setBaseUrl('/anov-gis-sdk/resources/');
122
+ const viewer = new ANOVGIS.Viewer('cesiumContainer', { ... });
123
+ ```
124
+
125
+ ---
126
+
127
+ ## P4. webpack 5 + Cesium UMD AMD 报错
128
+
129
+ `@anov/gis` 1.8.1+ 的 dist 是 webpack 5 UMD 产物,CesiumPrependPlugin 把 cesium 1.143 的 IIFE 拼在前面。webpack 5 静态扫描时会把 cesium URI 模块里 AMD 分支字符串依赖当成真实依赖解析:
130
+
131
+ ```
132
+ Module not found: Error: Can't resolve './punycode' in '.../node_modules/@anov/gis/dist'
133
+ Module not found: Error: Can't resolve './IPv6' in '.../node_modules/@anov/gis/dist'
134
+ Module not found: Error: Can't resolve './SecondLevelDomains' in '.../node_modules/@anov/gis/dist'
135
+ ```
136
+
137
+ **消费方临时方案**——必须在消费项目的 `vue.config.js`(或等价的 webpack 配置)加:
138
+
139
+ ```js
140
+ // ✅ 兜底:忽略 AMD 字符串依赖
141
+ const webpack = require('webpack');
142
+ new webpack.IgnorePlugin({
143
+ resourceRegExp: /^\.\/(punycode|IPv6|SecondLevelDomains)$/,
144
+ // 必须加 contextRegExp,否则 urijs 的真实 require('./punycode') 也会被忽略
145
+ contextRegExp: /[\\/]@anov[\\/]gis[\\/]/
146
+ })
147
+ ```
148
+
149
+ 误判信号:报这三个错且路径在 `@anov/gis/dist` 下,**且** `urijs/src/URI.js` 不在错误栈中。若忽略 `contextRegExp`,运行时会在 `urijs/src/URI.js:26` 报同样的错。根因修复需在 SDK 端改 `CesiumPrependPlugin`,把 cesium.js 用 `new Function(atob(b64))` 包成字符串执行,避免 webpack 静态扫描。
150
+
151
+ ---
152
+
153
+ <a id="p5-materialproperty"></a>
154
+
155
+ ## P5. MaterialProperty 子字段赋值不更新(Critical)
156
+
157
+ Cesium 的 `MaterialProperty` 子字段都是 `Cesium.createPropertyDescriptor` 包出的 setter。**子字段直接改(`.x` / `.rgb` / `.red` 等)触发不了 setter 内部的 `definitionChanged`**,渲染管线收不到 dirty 通知,GPU uniform 永远停在旧值——看上去"改值不生效"。
158
+
159
+ ### ❌ 错:改子字段
160
+
161
+ ```js
162
+ mat.color.red = 0.5; // uniform 不变
163
+ mat.color.rgb = [1,0,0]; // uniform 不变
164
+ mat.repeat.x = 200; // uniform 不变
165
+ ```
166
+
167
+ ### ✅ 对:整体赋新对象
168
+
169
+ ```js
170
+ mat.color = new Cesium.Color(0.5, 0, 0, 1); // 走 setter → definitionChanged → uniform 更新
171
+ mat.repeat = new Cesium.Cartesian2(200, 1); // 走 setter → uniform 更新
172
+ mat.image = newUrl; // 基础类型 / 字符串整体赋值同理
173
+ ```
174
+
175
+ **为什么对**:`createPropertyDescriptor` 在 setter 里主动触发 `definitionChanged.raiseEvent()`,整体赋值必经 setter;子字段赋值是 JS 引擎直接改闭包内的 `_字段`,完全绕过。
176
+
177
+ **来源**:memory `cesium_material_property_setter_pattern.md`;SDK `WallFlowMaterialProperty.js` L46-50 注册的 `color` descriptor 已验证(注:原文档误把这条说成"SDK 没注册通知路径",已纠正)。
178
+
179
+ ---
180
+
181
+ ## P6. file:// 协议不可用
182
+
183
+ Cesium 用 Web Workers + 大量 fetch 加载静态资源(`Workers/`、`Assets/`、`ThirdParty/`)。直接 `file://` 双击打开 HTML 时:
184
+
185
+ - Workers 被浏览器同源策略拦截(`http://`/`https://` 之外无法 fork worker)
186
+ - `Cesium.buildModuleUrl` 把 `Assets/...` 解析成 `file:///...` 路径,浏览器拒绝 fetch
187
+ - 表现:白屏、控制台 `Failed to construct 'Worker'` 或 `CORS` 报错
188
+
189
+ ```js
190
+ // ❌ 直接双击 file.html 不工作
191
+ file:///path/to/project/index.html
192
+
193
+ // ✅ 必须经 HTTP 服务
194
+ python -m http.server 8080 // 或 nginx / dev server
195
+ http://localhost:8080/index.html
196
+ ```
197
+
198
+ ---
199
+
200
+ ## P7. window.Cesium 全局变量
201
+
202
+ `@anov/gis` SDK 启动时主动 `window.Cesium = Cesium`(详见本 skill P7 节,不引用具体行号),并把 Cesium 上**非下划线开头**的所有成员挂到 `ANOVGIS` 顶层(如 `ANOVGIS.Color` / `ANOVGIS.Cartesian3` / `ANOVGIS.Viewer`)。但 **`Cesium` 对象本身** 没有作为 key 挂到 `ANOVGIS` 上 —— `ANOVGIS.Cesium` 是 `undefined`。
203
+
204
+ ```js
205
+ // ✅ 唯一推荐(CDN/UMD/ESM 全场景通用)
206
+ const Cesium = window.Cesium
207
+ const color = new Cesium.Color(1, 0, 0, 1)
208
+
209
+ // ✅ Cesium 成员走 ANOVGIS 顶层(SDK 已聚合)
210
+ const color = new ANOVGIS.Color(1, 0, 0, 1)
211
+
212
+ // ❌ ANOVGIS.Cesium 是 undefined(v1.8.6 SDK 没把 Cesium 对象本身挂上去)
213
+ const color = new ANOVGIS.Cesium.Color(1, 0, 0, 1) // TypeError: Cannot read properties of undefined
214
+
215
+ // ❌ 不要重复装 cesium 包
216
+ import Cesium from 'cesium'
217
+ ```
218
+
219
+ **Cesium 私有成员(`_` 开头)**:`@anov/gis` 不会挂到 `ANOVGIS` 上,必须走 `window.Cesium`。例如 `window.Cesium.ArcType.GEODESIC`。
220
+
221
+ ---
222
+
223
+ ## P8. SDK 包装的 ImageryLayer 不透出 Cesium 原生方法
224
+
225
+ `@anov/gis` 的 `ImageryLayerFactory.create*ImageryLayer()`(createTdt / createAmap / createArcGis / createUrlTemplate / createSingleTile 等)返回的是 **SDK 包装对象**,**不会透出**底层 `Cesium.ImageryLayer` 的所有方法。
226
+
227
+ **常见错误**(claude方式 项目实测,2026-09-17):
228
+
229
+ ```js
230
+ const cva = ANOVGIS.ImageryLayerFactory.createTdtImageryLayer({ style: 'cva', key })
231
+ cva.raiseToTop() // TypeError: cva.raiseToTop is not a function
232
+ cva.lowerToBottom() // TypeError: cva.lowerToBottom is not a function
233
+ cva.destroy() // TypeError: cva.destroy is not a function
234
+ ```
235
+
236
+ **正确做法**:通过 `viewer.imageryLayers` 上的方法操作(这些方法接收 SDK 包装对象,内部会 unwrap)。
237
+
238
+ ```js
239
+ viewer.imageryLayers.raiseToTop(cva)
240
+ viewer.imageryLayers.lowerToBottom(cva)
241
+ viewer.imageryLayers.remove(cva, true) // 第 2 个参数 destroy=true
242
+ ```
243
+
244
+ **例外(SDK 已透出的常用属性)**:可直接读写
245
+
246
+ ```js
247
+ cva.show = false // 显隐
248
+ cva.alpha = 0.6 // 透明度
249
+ cva.brightness = 2 // 亮度
250
+ cva.contrast = 1.5 // 对比度
251
+ cva.hue = 0.1 // 色调
252
+ cva.saturation = 0.8 // 饱和度
253
+ cva.gamma = 1.2 // 伽马
254
+ cva.splitDirection = 0.5 // 卷帘方向
255
+ ```
256
+
257
+ 判断依据:SDK 的 `wrapper.js` 内部只把这些常用属性通过 `Object.defineProperty` 透出到包装对象;其他方法只能走 `viewer.imageryLayers.*` 间接调用。
258
+
259
+ ---
260
+
261
+ ## P9. vite-plugin-static-copy@1.x ESM-only 与 Vite 配置 CJS 加载冲突(v0.2.7 加)
262
+
263
+ `vite-plugin-static-copy` 从 v1.0.0 起改为 **ESM-only**(`package.json` 含 `"type": "module"`,无 CJS 入口)。如果项目 `package.json` 没声明 `"type": "module"`,Vite 加载 `vite.config.ts` 时会用 CJS `require()` 拉这个插件 → 直接报错。
264
+
265
+ **常见错误**(claude方式 项目实测,2026-09-23):
266
+
267
+ ```
268
+ X [ERROR] "vite-plugin-static-copy" resolved to an ESM file. ESM file cannot be loaded by `require`.
269
+ See https://vite.dev/guide/troubleshooting.html#this-package-is-esm-only for more details.
270
+ [plugin externalize-deps]
271
+
272
+ node_modules/esbuild/lib/main.js:1225:27:
273
+ 1225 │ let result = await callback({
274
+ ╵ ^
275
+ ```
276
+
277
+ **两条修复路径**(任选其一):
278
+
279
+ ```json
280
+ // ✅ A. 升级到最新版 vite-plugin-static-copy (v4.x 已与 Vite 7 配套)
281
+ {
282
+ "devDependencies": {
283
+ "vite-plugin-static-copy": "^4.1.1" // ← 必须 >=1.x,但建议直接上 4.x
284
+ }
285
+ }
286
+ ```
287
+
288
+ ```json
289
+ // ✅ B. 项目级声明 ESM
290
+ {
291
+ "type": "module", // ← 让 Node 把 vite.config.ts 当 ESM 处理
292
+ ...
293
+ }
294
+ ```
295
+
296
+ **完整修复(推荐组合)**:两条一起做,永久免疫。
297
+
298
+ ```json
299
+ {
300
+ "type": "module",
301
+ "devDependencies": {
302
+ "vite-plugin-static-copy": "^4.1.1"
303
+ }
304
+ }
305
+ ```
306
+
307
+ **避坑要点**:
308
+
309
+ - ❌ 不要试图降级到 `vite-plugin-static-copy@^0.17.0`(v0 是 CJS 兼容但有别的问题:拷贝路径处理、watch 模式不稳定,已被 hainan / neimengnongken 等项目淘汰)
310
+ - ❌ 不要省略 `"type": "module"` —— 单改版本号在某些 npm 安装环境下仍会复现该错(Vite 加载链中 node 端是 CJS 还是 ESM 取决于最近 `package.json`)
311
+ - ✅ **默认模板**:见 [`using-anovgis` 模板 · 组合 1](../using-anovgis/templates/combo1-vue3-vite.md)(Vue 3 + Vite + 天地图 + 内网本地 SDK),已对齐 hainan 2026-09 实战版本
312
+
313
+ ---
314
+
315
+ ## P10. Vite 7 + 裸 `@anov/gis` 预构建撞名——虚惊一场,真凶是 baseLayer(v0.2.7 加,v0.2.10 定论)
316
+
317
+ `@anov/gis@1.8.6` 只暴露 `dist/index.js`(典型 UMD 包,**无 `module` 字段**)。Vite 默认走 `optimizeDeps` 预构建(esbuild 把 UMD 转 ESM)。在重命名单作用域 UMD 时,**esbuild 把不同源的两处函数都命名为 `Yi`**(vite 7 预构建 bundle 中 L41291 `Cesium.ImageryLayer` + L103228 SDK helper;vite 8 中 L53258 + L133250)。
318
+
319
+ **v0.2.10 定论(playwright 实测)**:撞名在 vite 7 / vite 8 预构建 bundle 中都存在(都是 2 处 `function Yi(`),且都在嵌套函数的不同层级(实测 -62 vs -56),**不同 scope,不 shadow,原形链完整**——撞名只是表象,不是 bug。v0.2.7 ~ v0.2.9 把报错归因于「vite 7 撞名 shadow」是**误诊**:真凶是构造时传的 `baseLayer: ANOVGIS.ImageryType.TDT`——枚举是字符串 `'tdt'`,SDK 把它塞进影像图层集合后,字符串上所有图层方法都崩(`_layerIndex`、`queueReprojectionCommands` 是同一根因的两张脸)。**删 baseLayer 行后,vite 7 + 裸 @anov/gis playwright 零报错**(vite 8 官方模板同样零报错——升不升 vite 8 都行,只要不传 baseLayer)。
320
+
321
+ **常见错误**(claude方式 项目实测,2026-09-23,vite 7 触发):
322
+
323
+ ```
324
+ TypeError: t[n].queueReprojectionCommands is not a function
325
+ at fa.queueReprojectionCommands (http://localhost:5174/node_modules/.vite/deps/@anov_gis.js?v=xxx:54620:56)
326
+ at e.initialize (http://localhost:5174/node_modules/.vite/deps/@anov_gis.js?v=xxx:72283:31)
327
+ at Qm.beginFrame (http://localhost:5174/node_modules/.vite/deps/@anov_gis.js?v=xxx:73107:119)
328
+ ```
329
+
330
+ **特征** —— 错误栈里出现 `queueReprojectionCommands` / `initialize` / `beginFrame`,且 `http://localhost:5174/node_modules/.vite/deps/@anov_gis.js` 的 hash 每次启动会变(说明是预构建产物)。
331
+
332
+ **实证排查**(claude方式 项目 09:23 复现):
333
+
334
+ ```bash
335
+ grep -c "function Yi(" node_modules/.vite/deps/@anov_gis.js
336
+ # vite 7: 2(撞名,不同 scope,不 shadow —— v0.2.10 实测零报错,之前的"同 scope"判断是误诊)
337
+ # vite 8: 2(同上,官方模板零报错)
338
+ ```
339
+
340
+ **修复路径**(v0.2.10:删 baseLayer 行就够——A/B/C 都是治不存在病的药,D 可选不强制):
341
+
342
+ ```ts
343
+ // ❌ A. 仅加 esbuildOptions.target: 'esnext' — 实测不够
344
+ export default defineConfig({
345
+ optimizeDeps: { esbuildOptions: { target: 'esnext' } },
346
+ })
347
+ // `target: 'esnext'` 只关闭 class transform, 不阻止 scope-based 重命名撞名
348
+ ```
349
+
350
+ ```ts
351
+ // ❌ B. 仅加 @anov/vite-plugins — 实测不够
352
+ import anovVitePlugin from '@anov/vite-plugins'
353
+ export default defineConfig({
354
+ plugins: [vue(), anovVitePlugin(), viteStaticCopy({ ... })],
355
+ })
356
+ // 它处理 layout-designer / HMR 周边, 但不阻止 esbuild 预构建撞名
357
+ // 而且 hello earth 不需要 layout-designer
358
+ ```
359
+
360
+ ```ts
361
+ // ❌ C. exclude @anov/gis from optimizeDeps — v0.2.9 撤销(v0.2.7.2 标为推荐,实际引入新错)
362
+ import { viteStaticCopy } from 'vite-plugin-static-copy'
363
+
364
+ export default defineConfig({
365
+ plugins: [
366
+ vue(),
367
+ viteStaticCopy({
368
+ targets: [{
369
+ src: 'node_modules/@anov/gis/dist/resources/**/*',
370
+ dest: 'anov-gis-sdk',
371
+ rename: { stripBase: 5 },
372
+ }],
373
+ }),
374
+ ],
375
+ optimizeDeps: {
376
+ exclude: ['@anov/gis'], // ⚠️ v0.2.9 撤销:浏览器报 `does not provide an export named 'default'`
377
+ },
378
+ })
379
+ // UMD 包没有 default 导出, 浏览器 ESM `import ANOVGIS from '@anov/gis'` 找不到
380
+ ```
381
+
382
+ ```ts
383
+ // ✅ D. 官方模板默认 vite 8(v0.2.10:vite 7 实测也零报错,不强制升)
384
+ import { fileURLToPath, URL } from 'node:url'
385
+ import { defineConfig } from 'vite'
386
+ import vue from '@vitejs/plugin-vue'
387
+ import { viteStaticCopy } from 'vite-plugin-static-copy'
388
+
389
+ export default defineConfig({
390
+ plugins: [
391
+ vue(),
392
+ viteStaticCopy({
393
+ targets: [{
394
+ src: 'node_modules/@anov/gis/dist/resources/**/*',
395
+ dest: 'anov-gis-sdk',
396
+ rename: { stripBase: 5 },
397
+ }],
398
+ }),
399
+ ],
400
+ resolve: {
401
+ alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) },
402
+ },
403
+ })
404
+ // vite 8 预构建 bundle 中 `function Yi(` 撞名在嵌套不同 scope (-62 vs -56),不 shadow,原形链完整
405
+ ```
406
+
407
+ **判断路径选择**:
408
+
409
+ | 场景 | 推荐 |
410
+ |---|---|
411
+ | Vue 3 + Vite **7 或 8** + 裸 `@anov/gis` 1.8.6 | **删 baseLayer 行**(v0.2.10 定论:vite 7 实测零报错;官方模板 vite 8 同样零报错,升不升都行。**v0.2.12 起默认模板统一用官方 vite 8 版本号**) |
412
+ | 任何构造时传 `baseLayer: ANOVGIS.ImageryType.*` | ❌ 必崩(`_layerIndex on string 'tdt'`),删掉 |
413
+ | hainan 之类全套 Anov SDK(`@anov/core` 等) | 同样删 baseLayer;vite 版本随意 |
414
+
415
+ **避坑要点**:
416
+
417
+ - ❌ A 路径(仅 `esbuildOptions.target: 'esnext'`)—— 治标不治本,且根本不必要
418
+ - ❌ B 路径(仅 `@anov/vite-plugins`)—— 不必要,且 hello earth 不需要
419
+ - ❌ **C 路径(`optimizeDeps.exclude: ['@anov/gis']`)** —— 反而引入 UMD 无 default 导出错,浏览器 ESM import 失败
420
+ - ❌ 不要 `optimizeDeps.include: ['@anov/gis']`(v0.2.9 说强制预构建必现 P10——v0.2.10 证实预构建本身无害,但 include 也没必要)
421
+ - ❌ 不要把 `@anov/cic-standard-sdk` 留预构建(`@anov/vite-plugins` 的依赖,会拉成两份 ESM)
422
+ - ✅ **默认模板**:见 [`using-anovgis` 模板 · 组合 1](../using-anovgis/templates/combo1-vue3-vite.md)(v0.2.12 从 0 创建项目统一走 Vue 3 + Vite 8 官方版本号 + 无 optimizeDeps + 无 baseLayer)
423
+ - ✅ 验证命令:用 playwright 加载页面,等 5s 检查 `console.error` 应为空(**不要只 curl 就 commit**,P11)
424
+
425
+ ---
426
+
427
+ ## P11. 验证流程:禁止"curl 没过 vite 启动就声称修复"(v0.2.9 加,meta 陷阱)
428
+
429
+ v0.2.7.2 教训:当时给 `optimizeDeps.exclude: ['@anov/gis']` 加了 fallback,**只跑了 curl + 检查 bundle 完整性**(4 处 `queueReprojectionCommands` 都在),**没真正用 playwright 加载页面**就 commit + CHANGELOG。**实际运行时浏览器报 `does not provide an export named 'default'`**(UMD 包没有 default 导出)。
430
+
431
+ **禁止的反模式**:
432
+ - ❌ 只 `curl http://localhost:5173/` 看 HTTP 200 就声称"修复"
433
+ - ❌ 只 `grep -c "queueReprojectionCommands" ...prebuilt.js` 看到方法存在就声称"修复"
434
+ - ❌ 只 `vite build` 成功就声称"修复"
435
+ - ❌ 不写端到端测试就直接把"修复"写进 CHANGELOG
436
+
437
+ **正确的验证流程**(任何声称"修复"的提交**前**必跑):
438
+
439
+ ```python
440
+ from playwright.sync_api import sync_playwright
441
+ import time
442
+
443
+ with sync_playwright() as p:
444
+ browser = p.chromium.launch()
445
+ page = browser.new_page()
446
+ errors = []
447
+ page.on('pageerror', lambda e: errors.append(e.message))
448
+ page.on('console', lambda m: errors.append(m.text) if m.type == 'error' else None)
449
+ page.goto('http://localhost:5173/', wait_until='networkidle', timeout=30000)
450
+ time.sleep(5) # 等 viewer 初始化 + 首帧渲染
451
+ print(f'errors: {len(errors)}')
452
+ for e in errors[:5]: print(f' - {e[:200]}')
453
+ has_canvas = page.evaluate("!!document.querySelector('canvas')")
454
+ print(f'canvas: {has_canvas}') # 必须 True
455
+ cesium = page.evaluate("!!window.Cesium")
456
+ print(f'cesium: {cesium}') # 必须 True
457
+ browser.close()
458
+ # 期望: errors: 0, canvas: True, cesium: True
459
+ ```
460
+
461
+ **Why**: vite 启动成功 ≠ 浏览器加载成功 ≠ viewer 渲染成功 ≠ Cesium 类可用。任何一步都可能引入新错。`curl` 只看到 HTTP 层,prebuild 完整性只看到 esbuild 输出层,**只有 playwright 加载才能覆盖整个链路**。
462
+
463
+ **触发场景**:
464
+ - v0.2.7.2:exclude 修复 P10(已撤销)—— curl 通过但 playwright 报 UMD 无 default
465
+ - 任何 UMD / CJS / ESM 互操作修复 —— 都是运行时才能验证
466
+
467
+ **避坑要点**:
468
+ - ❌ 永远不要用「vite 启动 + HTTP 200 + bundle 完整性」三件套就 commit "修复"
469
+ - ✅ 真正跑 playwright 才算验证通过
470
+ - ✅ 验证脚本要进 skill(不光是本次用,下次类似修复也要复用)
471
+
472
+ ## P12. baseLayerPicker 与默认底图的隐式绑定(v0.2.14 加)
473
+
474
+ SDK v1.8.6 把「是否注入默认底图」与 `baseLayerPicker` 开关**强绑定**——这是隐式契约,文档里没明说,踩坑极隐蔽:
475
+
476
+ | `baseLayerPicker` | 默认底图行为 | 是否需要 `addImageryProvider` |
477
+ |---|---|---|
478
+ | `true`(默认) | SDK 构造时自动 add 天地图影像 + 注记 | **不用** |
479
+ | `false` | SDK **不注入任何底图**,`viewer.imageryLayers.length === 0` | **必须**显式 `addImageryProvider` |
480
+
481
+ **典型错误**(skill_test 2026-10-28 实测,"参考图片 1.png 实现智慧水利大屏"项目):
482
+
483
+ ```js
484
+ // ❌ baseLayerPicker:false → 没底图 → 黑屏
485
+ const viewer = new ANOVGIS.Viewer('cesiumContainer', {
486
+ baseLayerPicker: false, // ← 隐式关闭了默认底图
487
+ geocoderType: ANOVGIS.GeocoderType.TIANDITU,
488
+ });
489
+ viewer.scene.globe.baseColor = '#02060f'; // ← 再把"球出来了"的视觉信号屏蔽
490
+ // 结果:深色背景 + 无贴图 = 一片漆黑,无任何 console 报错
491
+ ```
492
+
493
+ **两条修复路径任选其一**:
494
+
495
+ ```js
496
+ // ✅ 路 A:开 baseLayerPicker(让 SDK 自动加天地图)
497
+ // 代价:右上角会出底图切换控件
498
+ new ANOVGIS.Viewer('cesiumContainer', {
499
+ baseLayerPicker: true,
500
+ geocoderType: ANOVGIS.GeocoderType.TIANDITU,
501
+ });
502
+
503
+ // ✅ 路 B:关 baseLayerPicker + 显式 addImageryProvider(数字孪生大屏常用)
504
+ const viewer = new ANOVGIS.Viewer('cesiumContainer', {
505
+ baseLayerPicker: false,
506
+ });
507
+ viewer.imageryLayers.addImageryProvider(
508
+ ANOVGIS.ImageryLayerFactory.createImageryLayer(ANOVGIS.ImageryType.TDT, {
509
+ style: 'img', key: globalConfig.tdtKey
510
+ })
511
+ );
512
+ viewer.imageryLayers.addImageryProvider(
513
+ ANOVGIS.ImageryLayerFactory.createImageryLayer(ANOVGIS.ImageryType.TDT, {
514
+ style: 'cva', key: globalConfig.tdtKey
515
+ })
516
+ );
517
+ ```
518
+
519
+ **反模式**:
520
+ - ❌ `baseLayerPicker: false` + 没显式 `addImageryProvider` → 黑屏无报错
521
+ - ❌ `baseLayerPicker: false` + `globe.baseColor = 深色` → 把"球出来了"的唯一视觉信号也屏蔽,调试时反而看不出问题
522
+ - ❌ 假设 SDK 默认帮你加底图——实际只在 `baseLayerPicker: true` 时才加
523
+ - ❌ 把 `baseLayerPicker: false` 当作"清空底图"用——它只是不**注入**,不代表底图为空
524
+
525
+ **Why silent failure**:
526
+ SDK 不报错是因为对 SDK 来说"你没要底图,我没加"是合法行为;Cesium 1.143 起 Viewer 构造时不会自动尝试 Ion 加载,地球是空 ellipsoid,被 `globe.baseColor` 染色后呈现单一颜色。`console.error` 0 条,playwright 只看 `canvas` 数也是 True,所以常规自动化测试也会漏掉。
527
+
528
+ **与 P10 的关系**:
529
+ - **P10**:构造时传 `baseLayer: ANOVGIS.ImageryType.TDT`(字符串 `'tdt'`)→ 报 `_layerIndex on string 'tdt'` 错
530
+ - **P12**:构造时不传 baseLayer 也行,但**关掉 baseLayerPicker 后必须自己 addImageryProvider**
531
+ - **两条一起**:`baseLayerPicker: true`(默认)→ 啥都不用做就有天地图底图
532
+
533
+ **为什么这条放 P12 不放 `anovgis-viewer` § 2 Viewer 扩展参数表**:
534
+ 速览表只列字段含义,不涉及隐式契约。这种"两个看似无关字段联动"的行为适合陷阱库(pitfalls)——遇到现象(黑屏)回来查表命中。
535
+
536
+ **触发场景**:
537
+ - 数字孪生大屏 / 应急指挥 / 园区项目常用 `baseLayerPicker: false`(避免与业务 UI 抢位)——首次开发 100% 撞
538
+ - 大屏嵌入第三方系统时也常用 `false`(UI 由业务系统接管)
539
+ - 这两类场景都是 P12 的高发区
540
+
541
+ **排查顺序**:黑屏 + 无报错 + `canvas` 存在 + `imageryLayers.length === 0` → 第一怀疑 P12。
542
+
543
+ ## 排查顺序速记
544
+
545
+ | 症状 | 第一怀疑 |
546
+ |---|---|
547
+ | 地图黑屏、无报错 | P1(地形)/ **P12(baseLayerPicker 隐式关闭默认底图)** |
548
+ | 模型不显示/异常 | P2(fromGltfAsync)|
549
+ | Workers/Assets 404 | P3(setBaseUrl)|
550
+ | `@anov/gis/dist` 下 punycode/IPv6/SecondLevelDomains 解析失败 | P4(webpack AMD) |
551
+ | 材质属性改值不生效 | P5(setter 模式) |
552
+ | 白屏、CORS/Worker 报错 | P6(file://) |
553
+ | `Cesium` 命名空间 undefined | P7(window.Cesium) |
554
+ | 影像图层 `raiseToTop` / `lowerToBottom` / `destroy` undefined | **P8(包装对象不透出)** |
555
+ | `vite-plugin-static-copy` ESM 报错(无法被 require) | **P9(v1.x ESM-only)** |
556
+ | `TypeError: t[n].queueReprojectionCommands is not a function` | **P10(v0.2.10 定论:真凶 baseLayer 字符串,删掉即可——不是 vite 预构建错)** |
557
+ | `Cannot create property '_layerIndex' on string 'tdt'` | **v0.2.9 别在 Viewer 构造时传 `baseLayer: ANOVGIS.ImageryType.TDT`** |
558
+ | `does not provide an export named 'default'`(vite + exclude 后) | **P10(别用 `optimizeDeps.exclude`——UMD 无 default 导出)** |
559
+ | 仅 curl + 改 bundle 完整性就 commit 声称"修复",实际 playwright 报错 | **P11(验证后才能说修复)** |
560
+ | 黑屏 + 无 console 报错 + `imageryLayers.length === 0` | **P12(baseLayerPicker:false 后未 addImageryProvider)** |
561
+ | 代码里出现 `Cesium.UrlTemplateImageryProvider / WebMapService / WebMapTile` 自拼底图 | **P0(SDK 优先级)** |