@npm-questionpro/wick-ui-i18n 2.0.0-next.28 → 2.0.0-next.30
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/index.js +16 -0
- package/package.json +1 -1
- package/src/telemetry.js +123 -0
package/index.js
CHANGED
|
@@ -22,6 +22,7 @@ import {createFilter} from 'vite'
|
|
|
22
22
|
import {TranslationProcessor} from './src/processor.js'
|
|
23
23
|
import {transformFile} from './src/transform.js'
|
|
24
24
|
import {printReport} from './src/debug.js'
|
|
25
|
+
import {TelemetryCollector, collectComponents} from './src/telemetry.js'
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* @typedef {object} WickI18nOptions
|
|
@@ -40,6 +41,8 @@ import {printReport} from './src/debug.js'
|
|
|
40
41
|
* @returns {import('vite').Plugin}
|
|
41
42
|
*/
|
|
42
43
|
export default function wickuiI18nPlugin(options = {}) {
|
|
44
|
+
const telemetry = new TelemetryCollector()
|
|
45
|
+
|
|
43
46
|
const processor = new TranslationProcessor({
|
|
44
47
|
components: options.components || [],
|
|
45
48
|
ignoreComponents: options.ignoreComponents,
|
|
@@ -64,6 +67,7 @@ export default function wickuiI18nPlugin(options = {}) {
|
|
|
64
67
|
*/
|
|
65
68
|
configResolved(resolvedConfig) {
|
|
66
69
|
base = resolvedConfig.base
|
|
70
|
+
telemetry.scanVersions(resolvedConfig.root)
|
|
67
71
|
},
|
|
68
72
|
|
|
69
73
|
/**
|
|
@@ -73,6 +77,7 @@ export default function wickuiI18nPlugin(options = {}) {
|
|
|
73
77
|
buildStart() {
|
|
74
78
|
processor.dictionary.clear()
|
|
75
79
|
processor.entries = []
|
|
80
|
+
telemetry.reset()
|
|
76
81
|
},
|
|
77
82
|
|
|
78
83
|
/**
|
|
@@ -88,6 +93,7 @@ export default function wickuiI18nPlugin(options = {}) {
|
|
|
88
93
|
/\bwt\(/.test(code) ||
|
|
89
94
|
(processor.components.size > 0 && [...processor.components].some(c => code.includes(c))) ||
|
|
90
95
|
(processor.extractFromKeys.size > 0 && [...processor.extractFromKeys].some(k => code.includes(k)))
|
|
96
|
+
if (filter(id) && code.includes('Wu')) collectComponents(code, telemetry)
|
|
91
97
|
if (!filter(id) || !hasAnyTarget) return null
|
|
92
98
|
return transformFile(code, id, processor)
|
|
93
99
|
},
|
|
@@ -102,10 +108,20 @@ export default function wickuiI18nPlugin(options = {}) {
|
|
|
102
108
|
res.setHeader('Content-Type', 'application/json')
|
|
103
109
|
res.end(JSON.stringify(Object.fromEntries(processor.dictionary), null, 2))
|
|
104
110
|
})
|
|
111
|
+
server.middlewares.use(`${base}telemetry.json`, (_req, res) => {
|
|
112
|
+
res.setHeader('Content-Type', 'application/json')
|
|
113
|
+
res.end(JSON.stringify(telemetry.toJSON()))
|
|
114
|
+
})
|
|
105
115
|
},
|
|
106
116
|
|
|
107
117
|
/** Emit the translation dictionary as a build asset and print debug table. */
|
|
108
118
|
generateBundle() {
|
|
119
|
+
this.emitFile({
|
|
120
|
+
type: 'asset',
|
|
121
|
+
fileName: 'telemetry.json',
|
|
122
|
+
source: JSON.stringify(telemetry.toJSON()),
|
|
123
|
+
})
|
|
124
|
+
|
|
109
125
|
this.emitFile({
|
|
110
126
|
type: 'asset',
|
|
111
127
|
fileName: 'wick-ui-i18n.json',
|
package/package.json
CHANGED
package/src/telemetry.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Telemetry collector — scans consumer node_modules for
|
|
3
|
+
* @npm-questionpro/wick-ui-* versions and counts Wu* JSX component usages
|
|
4
|
+
* across the entire build. Emitted as `telemetry.json` at build time and
|
|
5
|
+
* served at `GET /telemetry.json` in dev.
|
|
6
|
+
*
|
|
7
|
+
* Intentionally isolated from i18n logic — no imports from other src/ files.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from 'node:fs'
|
|
11
|
+
import path from 'node:path'
|
|
12
|
+
import {parse} from '@babel/parser'
|
|
13
|
+
import _traverse from '@babel/traverse'
|
|
14
|
+
|
|
15
|
+
const traverse = _traverse.default || _traverse
|
|
16
|
+
|
|
17
|
+
/** Babel parser plugins applied to every file. */
|
|
18
|
+
const BABEL_PLUGINS = ['jsx', 'typescript']
|
|
19
|
+
|
|
20
|
+
export class TelemetryCollector {
|
|
21
|
+
constructor() {
|
|
22
|
+
/** @type {Record<string, string>} short name → version e.g. { lib: '2.0.0-next.29' } */
|
|
23
|
+
this.versions = {}
|
|
24
|
+
/** @type {Map<string, number>} PascalCase component name → total usage count */
|
|
25
|
+
this.counts = new Map()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Clear component counts — called on every buildStart so watch-mode rebuilds are clean. */
|
|
29
|
+
reset() {
|
|
30
|
+
this.counts.clear()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Increment the usage count for a Wu* component and record its prop names.
|
|
35
|
+
* @param {string} name - e.g. 'WuButton'
|
|
36
|
+
* @param {string[]} props - Prop names present on this usage e.g. ['variant', 'disabled']
|
|
37
|
+
*/
|
|
38
|
+
record(name, props = []) {
|
|
39
|
+
if (!this.counts.has(name)) this.counts.set(name, {count: 0, props: new Map()})
|
|
40
|
+
const entry = this.counts.get(name)
|
|
41
|
+
entry.count++
|
|
42
|
+
for (const prop of props) {
|
|
43
|
+
entry.props.set(prop, (entry.props.get(prop) ?? 0) + 1)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Scan the consumer's node_modules for all @npm-questionpro/wick-ui-* packages
|
|
49
|
+
* and record their versions. Called once in configResolved when root is known.
|
|
50
|
+
*
|
|
51
|
+
* @param {string} root - Vite project root
|
|
52
|
+
*/
|
|
53
|
+
scanVersions(root) {
|
|
54
|
+
const scopeDir = path.join(root, 'node_modules', '@npm-questionpro')
|
|
55
|
+
let entries
|
|
56
|
+
try {
|
|
57
|
+
entries = fs.readdirSync(scopeDir)
|
|
58
|
+
} catch {
|
|
59
|
+
// @npm-questionpro scope not present — skip silently
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
for (const name of entries) {
|
|
63
|
+
if (!name.startsWith('wick-ui-')) continue
|
|
64
|
+
try {
|
|
65
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(scopeDir, name, 'package.json'), 'utf8'))
|
|
66
|
+
const shortKey = name.replace('wick-ui-', '')
|
|
67
|
+
this.versions[shortKey] = pkg.version
|
|
68
|
+
} catch {
|
|
69
|
+
// malformed or missing package.json — skip
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Produce the final telemetry payload: versions first (sorted), then
|
|
76
|
+
* component counts (sorted alphabetically).
|
|
77
|
+
*
|
|
78
|
+
* @returns {Record<string, string|number>}
|
|
79
|
+
*/
|
|
80
|
+
toJSON() {
|
|
81
|
+
const sortedVersions = Object.fromEntries(Object.entries(this.versions).sort(([a], [b]) => a.localeCompare(b)))
|
|
82
|
+
const sortedCounts = Object.fromEntries(
|
|
83
|
+
[...this.counts.entries()]
|
|
84
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
85
|
+
.map(([name, {count, props}]) => [
|
|
86
|
+
name,
|
|
87
|
+
{
|
|
88
|
+
count,
|
|
89
|
+
props: Object.fromEntries([...props.entries()].sort(([a], [b]) => a.localeCompare(b))),
|
|
90
|
+
},
|
|
91
|
+
]),
|
|
92
|
+
)
|
|
93
|
+
return {...sortedVersions, ...sortedCounts}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Parse a single file and record all Wu* JSX component usages into the collector.
|
|
99
|
+
* Skips files that fail to parse without throwing.
|
|
100
|
+
*
|
|
101
|
+
* @param {string} code - Raw source of the file.
|
|
102
|
+
* @param {TelemetryCollector} collector
|
|
103
|
+
*/
|
|
104
|
+
export function collectComponents(code, collector) {
|
|
105
|
+
let ast
|
|
106
|
+
try {
|
|
107
|
+
ast = parse(code, {sourceType: 'module', plugins: BABEL_PLUGINS})
|
|
108
|
+
} catch {
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
traverse(ast, {
|
|
113
|
+
JSXOpeningElement(path) {
|
|
114
|
+
const name = path.node.name.name
|
|
115
|
+
if (typeof name === 'string' && name.startsWith('Wu')) {
|
|
116
|
+
const props = path.node.attributes
|
|
117
|
+
.filter(a => a.type === 'JSXAttribute' && typeof a.name?.name === 'string')
|
|
118
|
+
.map(a => a.name.name)
|
|
119
|
+
collector.record(name, props)
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
})
|
|
123
|
+
}
|