vccs 1.0.0-beta.1 → 1.0.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/README.md +11 -0
- package/dist/es/cartesian/bar/components/BarRectangles.mjs +1 -1
- package/dist/es/cartesian/brush/Brush.mjs +4 -0
- package/dist/es/cartesian/funnel/Funnel.mjs +1 -1
- package/dist/es/cartesian/scatter/Scatter.mjs +1 -1
- package/dist/es/chart/CellGridLayer.mjs +1 -1
- package/dist/es/chart/JourneySankey.mjs +1 -1
- package/dist/es/chart/Sankey.mjs +2 -2
- package/dist/es/chart/Sparkline.mjs +1 -1
- package/dist/es/chart/SunburstChart.mjs +1 -1
- package/dist/es/chart/Treemap.mjs +1 -1
- package/dist/es/events/sync.mjs +1 -2
- package/dist/es/events/usePointEvents.mjs +1 -1
- package/dist/es/model/tooltip.mjs +3 -5
- package/dist/es/polar/pie/Pie.mjs +1 -1
- package/dist/es/polar/radial-bar/RadialBar.mjs +1 -1
- package/dist/model/tooltip.d.ts +4 -18
- package/package.json +6 -2
- package/usage/catalog.json +235 -0
- package/usage/guides/charts/area-chart.md +56 -0
- package/usage/guides/charts/bar-chart.md +60 -0
- package/usage/guides/charts/bar-list.md +46 -0
- package/usage/guides/charts/calendar-heatmap.md +55 -0
- package/usage/guides/charts/cohort-chart.md +47 -0
- package/usage/guides/charts/composed-chart.md +70 -0
- package/usage/guides/charts/funnel-chart.md +58 -0
- package/usage/guides/charts/heatmap.md +51 -0
- package/usage/guides/charts/journey-sankey.md +49 -0
- package/usage/guides/charts/line-chart.md +60 -0
- package/usage/guides/charts/pie-chart.md +59 -0
- package/usage/guides/charts/radar-chart.md +69 -0
- package/usage/guides/charts/radial-bar-chart.md +54 -0
- package/usage/guides/charts/sankey.md +59 -0
- package/usage/guides/charts/scatter-chart.md +62 -0
- package/usage/guides/charts/sparkline.md +51 -0
- package/usage/guides/charts/sunburst-chart.md +66 -0
- package/usage/guides/charts/tracker.md +49 -0
- package/usage/guides/charts/treemap.md +68 -0
- package/usage/guides/core/installation.md +68 -0
- package/usage/guides/core/ssr-testing.md +50 -0
- package/usage/guides/core/typed-rows.md +47 -0
- package/usage/guides/core/vue-api.md +72 -0
- package/usage/vccs-usage.mjs +191 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* eslint-disable no-console -- a command-line report */
|
|
3
|
+
import { existsSync, readFileSync, realpathSync } from 'node:fs'
|
|
4
|
+
import { dirname, join } from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
|
|
7
|
+
const root = dirname(fileURLToPath(import.meta.url))
|
|
8
|
+
const packageJson = JSON.parse(readFileSync(join(root, '..', 'package.json'), 'utf8'))
|
|
9
|
+
|
|
10
|
+
const chartIds = [
|
|
11
|
+
'area-chart',
|
|
12
|
+
'bar-chart',
|
|
13
|
+
'bar-list',
|
|
14
|
+
'calendar-heatmap',
|
|
15
|
+
'cohort-chart',
|
|
16
|
+
'composed-chart',
|
|
17
|
+
'funnel-chart',
|
|
18
|
+
'heatmap',
|
|
19
|
+
'journey-sankey',
|
|
20
|
+
'line-chart',
|
|
21
|
+
'pie-chart',
|
|
22
|
+
'radar-chart',
|
|
23
|
+
'radial-bar-chart',
|
|
24
|
+
'sankey',
|
|
25
|
+
'scatter-chart',
|
|
26
|
+
'sparkline',
|
|
27
|
+
'sunburst-chart',
|
|
28
|
+
'tracker',
|
|
29
|
+
'treemap',
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
export function loadCatalog() {
|
|
33
|
+
const catalog = JSON.parse(readFileSync(join(root, 'catalog.json'), 'utf8'))
|
|
34
|
+
return { ...catalog, vccsVersion: packageJson.version }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function normalize(value) {
|
|
38
|
+
return value.toLowerCase().replaceAll('_', '-').trim()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const stopwords = new Set(['a', 'an', 'and', 'are', 'build', 'by', 'chart', 'create', 'for', 'from', 'graph', 'how', 'in', 'is', 'make', 'need', 'of', 'on', 'plot', 'show', 'the', 'to', 'use', 'using', 'want', 'with'])
|
|
42
|
+
|
|
43
|
+
function queryTokens(query) {
|
|
44
|
+
return normalize(query).split(/[^\p{L}\p{N}]+/u)
|
|
45
|
+
.filter(token => token.length > 1 && !stopwords.has(token))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function compact(value) {
|
|
49
|
+
return normalize(value).replace(/[^\p{L}\p{N}]/gu, '')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function summary(entry, score) {
|
|
53
|
+
return {
|
|
54
|
+
id: entry.id,
|
|
55
|
+
title: entry.title,
|
|
56
|
+
description: entry.description,
|
|
57
|
+
category: entry.category,
|
|
58
|
+
path: entry.path,
|
|
59
|
+
score,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function score(entry, query) {
|
|
64
|
+
const normalizedQuery = normalize(query)
|
|
65
|
+
if (!normalizedQuery)
|
|
66
|
+
return 0
|
|
67
|
+
|
|
68
|
+
const names = [entry.id, entry.title, ...entry.components]
|
|
69
|
+
const identity = normalize(names.join(' '))
|
|
70
|
+
const topics = normalize([entry.description, ...entry.useCases, ...entry.keywords].join(' '))
|
|
71
|
+
const exactName = names.some(name => compact(name) === compact(query))
|
|
72
|
+
let result = exactName ? 160 : identity.includes(normalizedQuery) ? 60 : 0
|
|
73
|
+
|
|
74
|
+
for (const token of queryTokens(query)) {
|
|
75
|
+
if (identity.includes(token))
|
|
76
|
+
result += 20
|
|
77
|
+
if (topics.includes(token))
|
|
78
|
+
result += 6
|
|
79
|
+
}
|
|
80
|
+
for (const phrase of [...entry.useCases, ...entry.keywords]) {
|
|
81
|
+
const normalizedPhrase = normalize(phrase)
|
|
82
|
+
if (normalizedPhrase.length > 1 && normalizedQuery.includes(normalizedPhrase))
|
|
83
|
+
result += 12
|
|
84
|
+
}
|
|
85
|
+
return result
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function searchCatalog(catalog, query, limit = 8) {
|
|
89
|
+
return catalog.guides
|
|
90
|
+
.map(entry => summary(entry, score(entry, query)))
|
|
91
|
+
.filter(entry => entry.score > 0)
|
|
92
|
+
.sort((a, b) => b.score - a.score || a.id.localeCompare(b.id))
|
|
93
|
+
.slice(0, limit)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function retrieveGuides(catalog, ids) {
|
|
97
|
+
return ids.map((id) => {
|
|
98
|
+
const entry = catalog.guides.find(guide => guide.id === id)
|
|
99
|
+
if (!entry)
|
|
100
|
+
throw new Error(`Unknown guide "${id}". Run the list command to see valid ids.`)
|
|
101
|
+
return readFileSync(join(root, entry.path), 'utf8')
|
|
102
|
+
}).join('\n\n---\n\n')
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function validateCatalog(catalog) {
|
|
106
|
+
const errors = []
|
|
107
|
+
const seen = new Set()
|
|
108
|
+
for (const entry of catalog.guides) {
|
|
109
|
+
if (seen.has(entry.id))
|
|
110
|
+
errors.push(`Duplicate guide id: ${entry.id}`)
|
|
111
|
+
seen.add(entry.id)
|
|
112
|
+
for (const field of ['title', 'description', 'category', 'path']) {
|
|
113
|
+
if (!entry[field]) {
|
|
114
|
+
errors.push(`${entry.id}: missing ${field}`)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (entry.path.includes('..') || !entry.path.startsWith('guides/'))
|
|
118
|
+
errors.push(`${entry.id}: unsafe guide path ${entry.path}`)
|
|
119
|
+
if (!existsSync(join(root, entry.path))) {
|
|
120
|
+
errors.push(`${entry.id}: missing file ${entry.path}`)
|
|
121
|
+
continue
|
|
122
|
+
}
|
|
123
|
+
if (entry.category === 'chart') {
|
|
124
|
+
const content = readFileSync(join(root, entry.path), 'utf8')
|
|
125
|
+
for (const marker of ['## Data shape', '## Minimal example', '## Chart-specific rules', '```vue']) {
|
|
126
|
+
if (!content.includes(marker)) {
|
|
127
|
+
errors.push(`${entry.id}: missing ${marker}`)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (!content.includes(entry.components[0]))
|
|
131
|
+
errors.push(`${entry.id}: example does not name ${entry.components[0]}`)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const actualChartIds = catalog.guides.filter(entry => entry.category === 'chart').map(entry => entry.id).sort()
|
|
136
|
+
for (const id of chartIds) {
|
|
137
|
+
if (!actualChartIds.includes(id)) {
|
|
138
|
+
errors.push(`Missing chart guide: ${id}`)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
for (const id of actualChartIds) {
|
|
142
|
+
if (!chartIds.includes(id)) {
|
|
143
|
+
errors.push(`Unexpected chart guide: ${id}`)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return errors
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function usage() {
|
|
150
|
+
return 'Usage: vccs-usage <search "query" | list | retrieve "id[,id...]" | validate>'
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function run(argv) {
|
|
154
|
+
const [command, argument] = argv
|
|
155
|
+
const catalog = loadCatalog()
|
|
156
|
+
if (command === 'search' && argument) {
|
|
157
|
+
const results = searchCatalog(catalog, argument)
|
|
158
|
+
if (!results.length)
|
|
159
|
+
throw new Error('No matching guides. Run the list command to browse all guides.')
|
|
160
|
+
console.log(JSON.stringify(results, null, 2))
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
if (command === 'list' && !argument) {
|
|
164
|
+
console.log(JSON.stringify(catalog.guides.map(entry => summary(entry, undefined)), null, 2))
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
if (command === 'retrieve' && argument) {
|
|
168
|
+
const ids = argument.split(',').map(id => id.trim()).filter(Boolean)
|
|
169
|
+
process.stdout.write(retrieveGuides(catalog, ids))
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
if (command === 'validate' && !argument) {
|
|
173
|
+
const errors = validateCatalog(catalog)
|
|
174
|
+
if (errors.length)
|
|
175
|
+
throw new Error(errors.join('\n'))
|
|
176
|
+
const chartCount = catalog.guides.filter(entry => entry.category === 'chart').length
|
|
177
|
+
console.log(`PASS: vccs ${catalog.vccsVersion}; ${catalog.guides.length} guides; ${chartCount} chart types; all chart guides have data and Vue examples.`)
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
throw new Error(usage())
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (process.argv[1] && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))) {
|
|
184
|
+
try {
|
|
185
|
+
run(process.argv.slice(2))
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
console.error(error.message)
|
|
189
|
+
process.exitCode = 1
|
|
190
|
+
}
|
|
191
|
+
}
|