@motionscript/geography 0.1.0-alpha.8 → 0.1.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.
- package/CHANGELOG.md +6 -0
- package/CREDITS.md +8 -8
- package/LICENSE +201 -201
- package/NOTICE +49 -49
- package/README.md +149 -149
- package/dist/border/format.js.map +1 -1
- package/dist/border/geo-border.js.map +1 -1
- package/dist/border/geometry.js.map +1 -1
- package/dist/border/index.d.ts +1 -1
- package/dist/border/index.js +1 -1
- package/dist/border/index.js.map +1 -1
- package/dist/border/loader.js.map +1 -1
- package/dist/border/registry.js.map +1 -1
- package/dist/border/selection.js.map +1 -1
- package/dist/border/simplify.js.map +1 -1
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/manifest.json +1 -1
- package/dist/index.js.map +1 -1
- package/dist/nodes.js.map +1 -1
- package/package.json +4 -4
- package/registry.json +16 -16
- package/src/border/format.ts +147 -147
- package/src/border/geo-border.ts +268 -268
- package/src/border/geometry.ts +463 -463
- package/src/border/index.ts +14 -14
- package/src/border/loader.ts +97 -97
- package/src/border/registry.ts +201 -201
- package/src/border/selection.ts +95 -95
- package/src/border/simplify.ts +110 -110
- package/src/index.ts +2 -2
- package/src/nodes.ts +16 -16
package/src/border/selection.ts
CHANGED
|
@@ -1,95 +1,95 @@
|
|
|
1
|
-
import type { GeoFeature } from "./format"
|
|
2
|
-
import { countryFileKey, geoFileEntry, statesFileKey } from "./loader"
|
|
3
|
-
import { geoRegistry } from "./registry"
|
|
4
|
-
|
|
5
|
-
export type GeoLevel = "country" | "state"
|
|
6
|
-
export type GeoFrame = "mainland" | "all"
|
|
7
|
-
|
|
8
|
-
const CODE = /^[A-Z]{2}(-[A-Z0-9]{1,3})?$/
|
|
9
|
-
|
|
10
|
-
/** `"fr, de"`, `["FR", "de"]` → `["FR", "DE"]`; unique, in order, empties dropped. */
|
|
11
|
-
export function normalizeIso(value: unknown): string[] {
|
|
12
|
-
const raw = Array.isArray(value) ? value : typeof value === "string" ? value.split(/[\s,;]+/) : []
|
|
13
|
-
const out: string[] = []
|
|
14
|
-
for (const item of raw) {
|
|
15
|
-
if (typeof item !== "string") continue
|
|
16
|
-
const code = item.trim().toUpperCase()
|
|
17
|
-
if (code && !out.includes(code)) out.push(code)
|
|
18
|
-
}
|
|
19
|
-
return out
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function isSubdivision(code: string): boolean {
|
|
23
|
-
return code.includes("-")
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** Every data file a selection reads, for `declareAssets`. Depends on nothing but its arguments. */
|
|
27
|
-
export function geoFilesFor(codes: readonly string[], level: GeoLevel): string[] {
|
|
28
|
-
const files = new Set<string>()
|
|
29
|
-
for (const code of codes) {
|
|
30
|
-
if (!CODE.test(code) || geoRegistry.defined(code)) continue
|
|
31
|
-
if (isSubdivision(code)) files.add(statesFileKey(code.slice(0, 2)))
|
|
32
|
-
else files.add(level === "state" ? statesFileKey(code) : countryFileKey(code))
|
|
33
|
-
}
|
|
34
|
-
return [...files]
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface GeoSelection {
|
|
38
|
-
features: GeoFeature[]
|
|
39
|
-
/** A file this selection needs has not landed yet. */
|
|
40
|
-
pending: boolean
|
|
41
|
-
/** Codes whose file loaded without them. */
|
|
42
|
-
unknown: string[]
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* The features a selection draws. A bare country code at state level is its
|
|
47
|
-
* first-level subdivisions — minus the remote ones when framing the mainland —
|
|
48
|
-
* or the country itself when it has none.
|
|
49
|
-
*/
|
|
50
|
-
export function selectGeoFeatures(codes: readonly string[], level: GeoLevel, frame: GeoFrame): GeoSelection {
|
|
51
|
-
const features: GeoFeature[] = []
|
|
52
|
-
const seen = new Set<string>()
|
|
53
|
-
const unknown: string[] = []
|
|
54
|
-
let pending = false
|
|
55
|
-
|
|
56
|
-
const add = (feature: GeoFeature) => {
|
|
57
|
-
if (seen.has(feature.code)) return
|
|
58
|
-
seen.add(feature.code)
|
|
59
|
-
features.push(feature)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
for (const code of codes) {
|
|
63
|
-
const defined = geoRegistry.defined(code)
|
|
64
|
-
if (defined) {
|
|
65
|
-
add(defined)
|
|
66
|
-
continue
|
|
67
|
-
}
|
|
68
|
-
if (!CODE.test(code)) {
|
|
69
|
-
unknown.push(code)
|
|
70
|
-
continue
|
|
71
|
-
}
|
|
72
|
-
const subdivision = isSubdivision(code)
|
|
73
|
-
const key = subdivision || level === "state" ? statesFileKey(code.slice(0, 2)) : countryFileKey(code)
|
|
74
|
-
const entry = geoFileEntry(key)
|
|
75
|
-
if (!entry || entry.status !== "ready") {
|
|
76
|
-
if (!entry) pending = true
|
|
77
|
-
continue
|
|
78
|
-
}
|
|
79
|
-
const file = entry.file.features
|
|
80
|
-
if (subdivision || level === "country") {
|
|
81
|
-
const feature = file.get(code)
|
|
82
|
-
if (feature) add(feature)
|
|
83
|
-
else unknown.push(code)
|
|
84
|
-
continue
|
|
85
|
-
}
|
|
86
|
-
const children = [...file.values()].filter(
|
|
87
|
-
(f) => f.parent === code && f.first && !(frame === "mainland" && f.remote)
|
|
88
|
-
)
|
|
89
|
-
if (children.length > 0) children.forEach(add)
|
|
90
|
-
else if (file.get(code)) add(file.get(code)!)
|
|
91
|
-
else unknown.push(code)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return { features, pending, unknown }
|
|
95
|
-
}
|
|
1
|
+
import type { GeoFeature } from "./format"
|
|
2
|
+
import { countryFileKey, geoFileEntry, statesFileKey } from "./loader"
|
|
3
|
+
import { geoRegistry } from "./registry"
|
|
4
|
+
|
|
5
|
+
export type GeoLevel = "country" | "state"
|
|
6
|
+
export type GeoFrame = "mainland" | "all"
|
|
7
|
+
|
|
8
|
+
const CODE = /^[A-Z]{2}(-[A-Z0-9]{1,3})?$/
|
|
9
|
+
|
|
10
|
+
/** `"fr, de"`, `["FR", "de"]` → `["FR", "DE"]`; unique, in order, empties dropped. */
|
|
11
|
+
export function normalizeIso(value: unknown): string[] {
|
|
12
|
+
const raw = Array.isArray(value) ? value : typeof value === "string" ? value.split(/[\s,;]+/) : []
|
|
13
|
+
const out: string[] = []
|
|
14
|
+
for (const item of raw) {
|
|
15
|
+
if (typeof item !== "string") continue
|
|
16
|
+
const code = item.trim().toUpperCase()
|
|
17
|
+
if (code && !out.includes(code)) out.push(code)
|
|
18
|
+
}
|
|
19
|
+
return out
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function isSubdivision(code: string): boolean {
|
|
23
|
+
return code.includes("-")
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Every data file a selection reads, for `declareAssets`. Depends on nothing but its arguments. */
|
|
27
|
+
export function geoFilesFor(codes: readonly string[], level: GeoLevel): string[] {
|
|
28
|
+
const files = new Set<string>()
|
|
29
|
+
for (const code of codes) {
|
|
30
|
+
if (!CODE.test(code) || geoRegistry.defined(code)) continue
|
|
31
|
+
if (isSubdivision(code)) files.add(statesFileKey(code.slice(0, 2)))
|
|
32
|
+
else files.add(level === "state" ? statesFileKey(code) : countryFileKey(code))
|
|
33
|
+
}
|
|
34
|
+
return [...files]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface GeoSelection {
|
|
38
|
+
features: GeoFeature[]
|
|
39
|
+
/** A file this selection needs has not landed yet. */
|
|
40
|
+
pending: boolean
|
|
41
|
+
/** Codes whose file loaded without them. */
|
|
42
|
+
unknown: string[]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The features a selection draws. A bare country code at state level is its
|
|
47
|
+
* first-level subdivisions — minus the remote ones when framing the mainland —
|
|
48
|
+
* or the country itself when it has none.
|
|
49
|
+
*/
|
|
50
|
+
export function selectGeoFeatures(codes: readonly string[], level: GeoLevel, frame: GeoFrame): GeoSelection {
|
|
51
|
+
const features: GeoFeature[] = []
|
|
52
|
+
const seen = new Set<string>()
|
|
53
|
+
const unknown: string[] = []
|
|
54
|
+
let pending = false
|
|
55
|
+
|
|
56
|
+
const add = (feature: GeoFeature) => {
|
|
57
|
+
if (seen.has(feature.code)) return
|
|
58
|
+
seen.add(feature.code)
|
|
59
|
+
features.push(feature)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for (const code of codes) {
|
|
63
|
+
const defined = geoRegistry.defined(code)
|
|
64
|
+
if (defined) {
|
|
65
|
+
add(defined)
|
|
66
|
+
continue
|
|
67
|
+
}
|
|
68
|
+
if (!CODE.test(code)) {
|
|
69
|
+
unknown.push(code)
|
|
70
|
+
continue
|
|
71
|
+
}
|
|
72
|
+
const subdivision = isSubdivision(code)
|
|
73
|
+
const key = subdivision || level === "state" ? statesFileKey(code.slice(0, 2)) : countryFileKey(code)
|
|
74
|
+
const entry = geoFileEntry(key)
|
|
75
|
+
if (!entry || entry.status !== "ready") {
|
|
76
|
+
if (!entry) pending = true
|
|
77
|
+
continue
|
|
78
|
+
}
|
|
79
|
+
const file = entry.file.features
|
|
80
|
+
if (subdivision || level === "country") {
|
|
81
|
+
const feature = file.get(code)
|
|
82
|
+
if (feature) add(feature)
|
|
83
|
+
else unknown.push(code)
|
|
84
|
+
continue
|
|
85
|
+
}
|
|
86
|
+
const children = [...file.values()].filter(
|
|
87
|
+
(f) => f.parent === code && f.first && !(frame === "mainland" && f.remote)
|
|
88
|
+
)
|
|
89
|
+
if (children.length > 0) children.forEach(add)
|
|
90
|
+
else if (file.get(code)) add(file.get(code)!)
|
|
91
|
+
else unknown.push(code)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return { features, pending, unknown }
|
|
95
|
+
}
|
package/src/border/simplify.ts
CHANGED
|
@@ -1,110 +1,110 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Visvalingam–Whyatt weights for a hand-defined ring, in the same buckets the
|
|
3
|
-
* generated data carries (`-2·log2` of the effective area in steradians), so a
|
|
4
|
-
* shape passed to `geoRegistry.define` answers to `detail` like any other.
|
|
5
|
-
*
|
|
6
|
-
* Planar in degrees with longitude scaled by latitude: accurate enough for the
|
|
7
|
-
* handful of shapes an author defines, where the generated data uses the exact
|
|
8
|
-
* spherical area.
|
|
9
|
-
*/
|
|
10
|
-
export function ringBuckets(points: Float64Array): Uint8Array {
|
|
11
|
-
const n = points.length / 2
|
|
12
|
-
const buckets = new Uint8Array(n)
|
|
13
|
-
if (n < 3) return buckets
|
|
14
|
-
|
|
15
|
-
const radians = Math.PI / 180
|
|
16
|
-
const weight = new Float64Array(n)
|
|
17
|
-
const prev = new Int32Array(n)
|
|
18
|
-
const next = new Int32Array(n)
|
|
19
|
-
for (let i = 0; i < n; i++) {
|
|
20
|
-
prev[i] = i - 1
|
|
21
|
-
next[i] = i + 1
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const area = (a: number, b: number, c: number): number => {
|
|
25
|
-
const cos = Math.cos(points[b * 2 + 1] * radians)
|
|
26
|
-
const ax = points[a * 2] * cos, ay = points[a * 2 + 1]
|
|
27
|
-
const bx = points[b * 2] * cos, by = points[b * 2 + 1]
|
|
28
|
-
const cx = points[c * 2] * cos, cy = points[c * 2 + 1]
|
|
29
|
-
return (Math.abs((bx - ax) * (cy - ay) - (cx - ax) * (by - ay)) / 2) * radians * radians
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const heap: number[] = []
|
|
33
|
-
const position = new Int32Array(n).fill(-1)
|
|
34
|
-
const less = (i: number, j: number) => weight[heap[i]] < weight[heap[j]]
|
|
35
|
-
const swap = (i: number, j: number) => {
|
|
36
|
-
;[heap[i], heap[j]] = [heap[j], heap[i]]
|
|
37
|
-
position[heap[i]] = i
|
|
38
|
-
position[heap[j]] = j
|
|
39
|
-
}
|
|
40
|
-
const up = (i: number) => {
|
|
41
|
-
while (i > 0) {
|
|
42
|
-
const parent = (i - 1) >> 1
|
|
43
|
-
if (!less(i, parent)) break
|
|
44
|
-
swap(i, parent)
|
|
45
|
-
i = parent
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
const down = (i: number) => {
|
|
49
|
-
for (;;) {
|
|
50
|
-
const l = 2 * i + 1
|
|
51
|
-
const r = l + 1
|
|
52
|
-
let smallest = i
|
|
53
|
-
if (l < heap.length && less(l, smallest)) smallest = l
|
|
54
|
-
if (r < heap.length && less(r, smallest)) smallest = r
|
|
55
|
-
if (smallest === i) return
|
|
56
|
-
swap(i, smallest)
|
|
57
|
-
i = smallest
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
const push = (i: number) => {
|
|
61
|
-
position[i] = heap.length
|
|
62
|
-
heap.push(i)
|
|
63
|
-
up(heap.length - 1)
|
|
64
|
-
}
|
|
65
|
-
const remove = (i: number) => {
|
|
66
|
-
const at = position[i]
|
|
67
|
-
const last = heap.pop()!
|
|
68
|
-
position[i] = -1
|
|
69
|
-
if (at < heap.length) {
|
|
70
|
-
heap[at] = last
|
|
71
|
-
position[last] = at
|
|
72
|
-
up(at)
|
|
73
|
-
down(position[last])
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
for (let i = 1; i < n - 1; i++) {
|
|
78
|
-
weight[i] = area(i - 1, i, i + 1)
|
|
79
|
-
push(i)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
let floor = 0
|
|
83
|
-
const final = new Float64Array(n)
|
|
84
|
-
final[0] = final[n - 1] = Infinity
|
|
85
|
-
while (heap.length > 0) {
|
|
86
|
-
const i = heap[0]
|
|
87
|
-
remove(i)
|
|
88
|
-
floor = Math.max(floor, weight[i])
|
|
89
|
-
final[i] = floor
|
|
90
|
-
const p = prev[i]
|
|
91
|
-
const q = next[i]
|
|
92
|
-
if (p >= 0) next[p] = q
|
|
93
|
-
if (q < n) prev[q] = p
|
|
94
|
-
for (const j of [p, q]) {
|
|
95
|
-
if (j <= 0 || j >= n - 1 || position[j] < 0) continue
|
|
96
|
-
remove(j)
|
|
97
|
-
weight[j] = area(prev[j], j, next[j])
|
|
98
|
-
push(j)
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
for (let i = 0; i < n; i++) buckets[i] = weightBucket(final[i])
|
|
103
|
-
return buckets
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export function weightBucket(weight: number): number {
|
|
107
|
-
if (weight === Infinity) return 0
|
|
108
|
-
if (!(weight > 0)) return 255
|
|
109
|
-
return Math.min(254, Math.max(1, Math.round(-2 * Math.log2(weight))))
|
|
110
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Visvalingam–Whyatt weights for a hand-defined ring, in the same buckets the
|
|
3
|
+
* generated data carries (`-2·log2` of the effective area in steradians), so a
|
|
4
|
+
* shape passed to `geoRegistry.define` answers to `detail` like any other.
|
|
5
|
+
*
|
|
6
|
+
* Planar in degrees with longitude scaled by latitude: accurate enough for the
|
|
7
|
+
* handful of shapes an author defines, where the generated data uses the exact
|
|
8
|
+
* spherical area.
|
|
9
|
+
*/
|
|
10
|
+
export function ringBuckets(points: Float64Array): Uint8Array {
|
|
11
|
+
const n = points.length / 2
|
|
12
|
+
const buckets = new Uint8Array(n)
|
|
13
|
+
if (n < 3) return buckets
|
|
14
|
+
|
|
15
|
+
const radians = Math.PI / 180
|
|
16
|
+
const weight = new Float64Array(n)
|
|
17
|
+
const prev = new Int32Array(n)
|
|
18
|
+
const next = new Int32Array(n)
|
|
19
|
+
for (let i = 0; i < n; i++) {
|
|
20
|
+
prev[i] = i - 1
|
|
21
|
+
next[i] = i + 1
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const area = (a: number, b: number, c: number): number => {
|
|
25
|
+
const cos = Math.cos(points[b * 2 + 1] * radians)
|
|
26
|
+
const ax = points[a * 2] * cos, ay = points[a * 2 + 1]
|
|
27
|
+
const bx = points[b * 2] * cos, by = points[b * 2 + 1]
|
|
28
|
+
const cx = points[c * 2] * cos, cy = points[c * 2 + 1]
|
|
29
|
+
return (Math.abs((bx - ax) * (cy - ay) - (cx - ax) * (by - ay)) / 2) * radians * radians
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const heap: number[] = []
|
|
33
|
+
const position = new Int32Array(n).fill(-1)
|
|
34
|
+
const less = (i: number, j: number) => weight[heap[i]] < weight[heap[j]]
|
|
35
|
+
const swap = (i: number, j: number) => {
|
|
36
|
+
;[heap[i], heap[j]] = [heap[j], heap[i]]
|
|
37
|
+
position[heap[i]] = i
|
|
38
|
+
position[heap[j]] = j
|
|
39
|
+
}
|
|
40
|
+
const up = (i: number) => {
|
|
41
|
+
while (i > 0) {
|
|
42
|
+
const parent = (i - 1) >> 1
|
|
43
|
+
if (!less(i, parent)) break
|
|
44
|
+
swap(i, parent)
|
|
45
|
+
i = parent
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const down = (i: number) => {
|
|
49
|
+
for (;;) {
|
|
50
|
+
const l = 2 * i + 1
|
|
51
|
+
const r = l + 1
|
|
52
|
+
let smallest = i
|
|
53
|
+
if (l < heap.length && less(l, smallest)) smallest = l
|
|
54
|
+
if (r < heap.length && less(r, smallest)) smallest = r
|
|
55
|
+
if (smallest === i) return
|
|
56
|
+
swap(i, smallest)
|
|
57
|
+
i = smallest
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const push = (i: number) => {
|
|
61
|
+
position[i] = heap.length
|
|
62
|
+
heap.push(i)
|
|
63
|
+
up(heap.length - 1)
|
|
64
|
+
}
|
|
65
|
+
const remove = (i: number) => {
|
|
66
|
+
const at = position[i]
|
|
67
|
+
const last = heap.pop()!
|
|
68
|
+
position[i] = -1
|
|
69
|
+
if (at < heap.length) {
|
|
70
|
+
heap[at] = last
|
|
71
|
+
position[last] = at
|
|
72
|
+
up(at)
|
|
73
|
+
down(position[last])
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
for (let i = 1; i < n - 1; i++) {
|
|
78
|
+
weight[i] = area(i - 1, i, i + 1)
|
|
79
|
+
push(i)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let floor = 0
|
|
83
|
+
const final = new Float64Array(n)
|
|
84
|
+
final[0] = final[n - 1] = Infinity
|
|
85
|
+
while (heap.length > 0) {
|
|
86
|
+
const i = heap[0]
|
|
87
|
+
remove(i)
|
|
88
|
+
floor = Math.max(floor, weight[i])
|
|
89
|
+
final[i] = floor
|
|
90
|
+
const p = prev[i]
|
|
91
|
+
const q = next[i]
|
|
92
|
+
if (p >= 0) next[p] = q
|
|
93
|
+
if (q < n) prev[q] = p
|
|
94
|
+
for (const j of [p, q]) {
|
|
95
|
+
if (j <= 0 || j >= n - 1 || position[j] < 0) continue
|
|
96
|
+
remove(j)
|
|
97
|
+
weight[j] = area(prev[j], j, next[j])
|
|
98
|
+
push(j)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
for (let i = 0; i < n; i++) buckets[i] = weightBucket(final[i])
|
|
103
|
+
return buckets
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function weightBucket(weight: number): number {
|
|
107
|
+
if (weight === Infinity) return 0
|
|
108
|
+
if (!(weight > 0)) return 255
|
|
109
|
+
return Math.min(254, Math.max(1, Math.round(-2 * Math.log2(weight))))
|
|
110
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "./border";
|
|
2
|
-
export { NODES } from "./nodes";
|
|
1
|
+
export * from "./border";
|
|
2
|
+
export { NODES } from "./nodes";
|
package/src/nodes.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { GeoBorder } from "./border";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Every node type this package publishes.
|
|
5
|
-
*
|
|
6
|
-
* A host registers these by handing the array to an engine —
|
|
7
|
-
* `new Engine(platform, { nodes: NODES })` — which reads each component's key.
|
|
8
|
-
* Defining a component only *declares* that key: nothing is registered by
|
|
9
|
-
* importing a module, so a registry holds exactly what its owner asked for.
|
|
10
|
-
*
|
|
11
|
-
* Listing the component **values** is also what survives bundling: this package
|
|
12
|
-
* declares `sideEffects: false`, and a document names a node type by string, so
|
|
13
|
-
* an import kept only for its side effect is exactly what gets shaken out.
|
|
14
|
-
*/
|
|
15
|
-
// Typed by name, so the declaration names each component rather than spelling out its type through core.
|
|
16
|
-
export const NODES: (typeof GeoBorder)[] = [GeoBorder];
|
|
1
|
+
import { GeoBorder } from "./border";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Every node type this package publishes.
|
|
5
|
+
*
|
|
6
|
+
* A host registers these by handing the array to an engine —
|
|
7
|
+
* `new Engine(platform, { nodes: NODES })` — which reads each component's key.
|
|
8
|
+
* Defining a component only *declares* that key: nothing is registered by
|
|
9
|
+
* importing a module, so a registry holds exactly what its owner asked for.
|
|
10
|
+
*
|
|
11
|
+
* Listing the component **values** is also what survives bundling: this package
|
|
12
|
+
* declares `sideEffects: false`, and a document names a node type by string, so
|
|
13
|
+
* an import kept only for its side effect is exactly what gets shaken out.
|
|
14
|
+
*/
|
|
15
|
+
// Typed by name, so the declaration names each component rather than spelling out its type through core.
|
|
16
|
+
export const NODES: (typeof GeoBorder)[] = [GeoBorder];
|