@uxf/velo 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.
Files changed (3) hide show
  1. package/README.md +80 -0
  2. package/cli.js +142 -0
  3. package/package.json +35 -0
package/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # @uxf/velo
2
+
3
+ A TypeScript dependency tree generator using Rust and WebAssembly.
4
+
5
+ ## Features
6
+ - Fast TypeScript/TSX parsing using `swc`.
7
+ - Aggregates i18n namespaces from components and their dependencies.
8
+ - Generates an optimized route-to-namespace map.
9
+ - Can be published to npm and used as a CLI.
10
+
11
+ ## Installation
12
+ ```bash
13
+ npm install -g @uxf/velo
14
+ ```
15
+
16
+ ## Usage
17
+ ```bash
18
+ uxf-velo <project-dir>
19
+ ```
20
+
21
+ Starting from the `src/pages/` directory (standard for Next.js), it builds a dependency graph and extracts i18n namespaces (e.g., from `t("ns:key")` or `<Trans i18nKey="ns:key" />`).
22
+
23
+ ## Build from Source
24
+ Requirements:
25
+ - Rust (latest)
26
+ - wasm-pack
27
+
28
+ ### Initialization
29
+ Before the first build, run:
30
+ ```bash
31
+ npm run init
32
+ ```
33
+ This ensures the Rust target for WebAssembly is installed and all dependencies are present.
34
+
35
+ ### Building
36
+ To build the package:
37
+ ```bash
38
+ npm run build
39
+ ```
40
+
41
+ ## Production Release and Publishing
42
+ This package includes a full publish pipeline:
43
+
44
+ 1. **Test the package**:
45
+ ```bash
46
+ npm run test
47
+ ```
48
+
49
+ 2. **Full Release (Automated)**:
50
+ ```bash
51
+ npm run release
52
+ ```
53
+ This script performs the following steps:
54
+ - Sets the Rust target for WebAssembly.
55
+ - Builds the WebAssembly package.
56
+ - Runs all tests.
57
+ - Increments the version (patch).
58
+ - Publishes to npm with public access.
59
+
60
+ Alternatively, you can manually run `npm publish`. The `prepublishOnly` script will automatically ensure the package is built and tested before being sent to the npm registry.
61
+
62
+ ## Output Format
63
+ The tool outputs a sorted JSON object mapping route keys to an array of unique, aggregated namespaces:
64
+ ```json
65
+ {
66
+ "*": [
67
+ "common",
68
+ "auth"
69
+ ],
70
+ "/": [
71
+ "auth",
72
+ "common",
73
+ "uxf-form-text-input"
74
+ ],
75
+ "/about": [
76
+ "about",
77
+ "common"
78
+ ]
79
+ }
80
+ ```
package/cli.js ADDED
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { globSync } = require('glob');
6
+ const wasm = require('./pkg/velo.js');
7
+ require.extensions['.ts'] = () => {};
8
+ require.extensions['.tsx'] = () => {};
9
+ require.extensions['.jsx'] = () => {};
10
+
11
+ const projectDir = process.argv[2] || '.';
12
+ const absoluteProjectDir = path.resolve(projectDir);
13
+
14
+ if (!fs.existsSync(absoluteProjectDir)) {
15
+ console.error(`Error: Project directory "${projectDir}" does not exist.`);
16
+ process.exit(1);
17
+ }
18
+
19
+ // Load tsconfig.json if it exists
20
+ let tsConfig = null;
21
+ const tsConfigPath = path.join(absoluteProjectDir, 'tsconfig.json');
22
+ if (fs.existsSync(tsConfigPath)) {
23
+ try {
24
+ const rawContent = fs.readFileSync(tsConfigPath, 'utf8');
25
+ // Simple comment removal (doesn't handle all cases, but should be enough for basic ones)
26
+ const cleanContent = rawContent.replace(/\/\/.*/g, '');
27
+ tsConfig = JSON.parse(cleanContent);
28
+ } catch (e) {
29
+ console.error(`Error: Failed to parse tsconfig.json (${tsConfigPath}):`, e);
30
+ process.exit(1);
31
+ }
32
+ } else {
33
+ console.error(`Error: tsconfig.json (${tsConfigPath}) not found in the project directory "${projectDir}".`);
34
+ process.exit(1);
35
+ }
36
+
37
+ // 1. Find entrypoints in 'src/pages/' directory
38
+ const entrypoints = globSync('src/pages/**/*.{ts,tsx}', {
39
+ cwd: absoluteProjectDir,
40
+ ignore: ['node_modules/**'],
41
+ nodir: true,
42
+ posix: true
43
+ }).map(f => path.resolve(absoluteProjectDir, f));
44
+
45
+ if (entrypoints.length === 0) {
46
+ console.warn('Warning: No .ts or .tsx files found in the "src/pages" directory.');
47
+ }
48
+
49
+ function readFile(filepath) {
50
+ try {
51
+ const ext = path.extname(filepath);
52
+ if (['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'].includes(ext) || ext === '') {
53
+ return fs.readFileSync(filepath, 'utf8');
54
+ }
55
+ } catch (e) {
56
+ // ignore
57
+ }
58
+ return null;
59
+ }
60
+
61
+ function resolveAlias(specifier) {
62
+ if (!tsConfig || !tsConfig.compilerOptions || !tsConfig.compilerOptions.paths) {
63
+ return null;
64
+ }
65
+ const { paths, baseUrl = '.' } = tsConfig.compilerOptions;
66
+ const absoluteBaseUrl = path.resolve(absoluteProjectDir, baseUrl);
67
+
68
+ for (const [alias, aliasPaths] of Object.entries(paths)) {
69
+ if (alias.endsWith('/*')) {
70
+ const prefix = alias.slice(0, -2);
71
+ if (specifier === prefix || specifier.startsWith(prefix + '/')) {
72
+ const subPath = specifier === prefix ? '' : specifier.slice(prefix.length + 1);
73
+ for (const aliasPath of aliasPaths) {
74
+ const resolvedAliasPath = aliasPath.replace('*', subPath);
75
+ const absolutePath = path.resolve(absoluteBaseUrl, resolvedAliasPath);
76
+ try {
77
+ return require.resolve(absolutePath);
78
+ } catch (e) {}
79
+ }
80
+ }
81
+ } else if (alias === specifier || specifier.startsWith(alias + '/')) {
82
+ const subPath = specifier === alias ? '' : specifier.slice(alias.length + 1);
83
+ for (const aliasPath of aliasPaths) {
84
+ const absolutePath = subPath ? path.resolve(absoluteBaseUrl, aliasPath, subPath) : path.resolve(absoluteBaseUrl, aliasPath);
85
+ try {
86
+ return require.resolve(absolutePath);
87
+ } catch (e) {}
88
+ }
89
+ }
90
+ }
91
+
92
+ return null;
93
+ }
94
+
95
+ function filterResolved(resolved) {
96
+ const nm = 'node_modules' + path.sep;
97
+ const uxf = nm + '@uxf' + path.sep;
98
+
99
+ const resolvedNmIndex = resolved.lastIndexOf(nm);
100
+ if (resolvedNmIndex !== -1 && !resolved.startsWith(uxf, resolvedNmIndex)) {
101
+ return null;
102
+ }
103
+
104
+ return resolved;
105
+ }
106
+
107
+ function resolveImport(specifier, parentPath) {
108
+ try {
109
+ // 1. Try alias resolution first
110
+ const aliasResolved = resolveAlias(specifier);
111
+ if (aliasResolved) {
112
+ return filterResolved(aliasResolved);
113
+ }
114
+
115
+ // 2. Try baseUrl resolution if not relative and baseUrl is set
116
+ if (!specifier.startsWith('.') && !path.isAbsolute(specifier) && tsConfig && tsConfig.compilerOptions && tsConfig.compilerOptions.baseUrl) {
117
+ const absoluteBaseUrl = path.resolve(absoluteProjectDir, tsConfig.compilerOptions.baseUrl);
118
+ try {
119
+ const baseUrlResolved = require.resolve(path.resolve(absoluteBaseUrl, specifier));
120
+ if (baseUrlResolved) {
121
+ return filterResolved(baseUrlResolved);
122
+ }
123
+ } catch (e) {}
124
+ }
125
+
126
+ const resolved = require.resolve(specifier, {
127
+ paths: [path.dirname(parentPath)]
128
+ });
129
+
130
+ return filterResolved(resolved);
131
+ } catch (e) {
132
+ return null;
133
+ }
134
+ }
135
+
136
+ try {
137
+ const tree = wasm.build_tree_map_js(entrypoints, absoluteProjectDir, readFile, resolveImport);
138
+ process.stdout.write(JSON.stringify(tree, null, 4) + '\n');
139
+ } catch (e) {
140
+ console.error('Error building dependency tree:', e);
141
+ process.exit(1);
142
+ }
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@uxf/velo",
3
+ "version": "0.1.0",
4
+ "description": "TypeScript dependency tree generator using Rust WebAssembly",
5
+ "main": "./pkg/velo.js",
6
+ "types": "./pkg/velo.d.ts",
7
+ "bin": {
8
+ "uxf-velo": "cli.js"
9
+ },
10
+ "scripts": {
11
+ "init": "rustup target add wasm32-unknown-unknown && npm install",
12
+ "build": "rustup target add wasm32-unknown-unknown && wasm-pack build --target nodejs",
13
+ "test": "node test_structure.js && node test_cli_format.js && node test_sort.js",
14
+ "lint": "cargo clippy -- -D warnings",
15
+ "fmt": "cargo fmt -- --check",
16
+ "prepublishOnly": "npm run build && npm run test",
17
+ "release": "npm run build && npm run test && npm version patch && npm publish --access public",
18
+ "dev": "node cli.js ./example"
19
+ },
20
+ "dependencies": {
21
+ "glob": "^11.1.0"
22
+ },
23
+ "files": [
24
+ "pkg/",
25
+ "cli.js"
26
+ ],
27
+ "devDependencies": {
28
+ "@headlessui/react": "^1.7.19",
29
+ "@uxf/form": "^11.110.0",
30
+ "@uxf/ui": "^11.110.0",
31
+ "react": "^18.3.1",
32
+ "typescript": "^6.0.2",
33
+ "wasm-pack": "^0.14.0"
34
+ }
35
+ }