@garrix82/reactgenie-lib 1.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.
- package/.env.example +22 -0
- package/.github/workflows/publish.yml +20 -0
- package/LICENSE.txt +201 -0
- package/README.md +621 -0
- package/babel.config.js +29 -0
- package/dist/adapters/__tests__/expo-router-adapter.test.d.ts +1 -0
- package/dist/adapters/expo-router-adapter.d.ts +16 -0
- package/dist/adapters/expo-router-adapter.js +521 -0
- package/dist/adapters/navigation-adapter.d.ts +20 -0
- package/dist/adapters/navigation-adapter.js +137 -0
- package/dist/audio-visualizer.d.ts +14 -0
- package/dist/audio-visualizer.js +123 -0
- package/dist/current-selection.d.ts +27 -0
- package/dist/current-selection.js +94 -0
- package/dist/errors.d.ts +19 -0
- package/dist/errors.js +37 -0
- package/dist/genie/DateTime.d.ts +66 -0
- package/dist/genie/DateTime.js +399 -0
- package/dist/genie/TimeDelta.d.ts +35 -0
- package/dist/genie/TimeDelta.js +169 -0
- package/dist/genie-view-wrapper.d.ts +1 -0
- package/dist/genie-view-wrapper.js +377 -0
- package/dist/hooks/__tests__/useSpeechRecognition.test.d.ts +1 -0
- package/dist/hooks/useSpeechRecognition.d.ts +28 -0
- package/dist/hooks/useSpeechRecognition.js +118 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +469 -0
- package/dist/logger.d.ts +23 -0
- package/dist/logger.js +597 -0
- package/dist/logger.remote.test.d.ts +0 -0
- package/dist/modality-provider-v2.d.ts +28 -0
- package/dist/modality-provider-v2.js +1321 -0
- package/dist/modality-provider.d.ts +22 -0
- package/dist/modality-provider.js +373 -0
- package/dist/native-visibility.d.ts +28 -0
- package/dist/native-visibility.js +50 -0
- package/dist/platform/VoiceRecognitionBar.d.ts +17 -0
- package/dist/platform/VoiceRecognitionBar.js +332 -0
- package/dist/platform/components.d.ts +32 -0
- package/dist/platform/components.js +351 -0
- package/dist/platform/events.d.ts +31 -0
- package/dist/platform/events.js +274 -0
- package/dist/platform/index.d.ts +3 -0
- package/dist/platform/index.js +39 -0
- package/dist/platform/types.d.ts +79 -0
- package/dist/platform/types.js +97 -0
- package/dist/react-decorators.d.ts +87 -0
- package/dist/react-decorators.js +368 -0
- package/dist/shared-store.d.ts +74 -0
- package/dist/shared-store.js +589 -0
- package/dist/speech-recognition/__tests__/speech-recognition-groq-transport.test.d.ts +1 -0
- package/dist/speech-recognition/__tests__/speech-recognition-native.test.d.ts +1 -0
- package/dist/speech-recognition/__tests__/speech-recognition-openai-native.test.d.ts +1 -0
- package/dist/speech-recognition/__tests__/speech-recognition-openai.test.d.ts +1 -0
- package/dist/speech-recognition/__tests__/speech-recognition-unified-import.test.d.ts +0 -0
- package/dist/speech-recognition/__tests__/speech-recognition-unified.test.d.ts +1 -0
- package/dist/speech-recognition/speech-recognition-groq.d.ts +21 -0
- package/dist/speech-recognition/speech-recognition-groq.js +409 -0
- package/dist/speech-recognition/speech-recognition-mlx.d.ts +15 -0
- package/dist/speech-recognition/speech-recognition-mlx.js +393 -0
- package/dist/speech-recognition/speech-recognition-native.d.ts +24 -0
- package/dist/speech-recognition/speech-recognition-native.js +632 -0
- package/dist/speech-recognition/speech-recognition-openai-native.d.ts +40 -0
- package/dist/speech-recognition/speech-recognition-openai-native.js +653 -0
- package/dist/speech-recognition/speech-recognition-openai.d.ts +39 -0
- package/dist/speech-recognition/speech-recognition-openai.js +718 -0
- package/dist/speech-recognition/speech-recognition-unified.d.ts +93 -0
- package/dist/speech-recognition/speech-recognition-unified.js +589 -0
- package/dist/speech-recognition/utils/groq-transcription.d.ts +41 -0
- package/dist/speech-recognition/utils/groq-transcription.js +382 -0
- package/dist/speech-recognition.d.ts +7 -0
- package/dist/speech-recognition.js +61 -0
- package/dist/voice-pipeline-telemetry.d.ts +26 -0
- package/dist/voice-pipeline-telemetry.js +15 -0
- package/garrix82-reactgenie-lib-1.3.0.tgz +0 -0
- package/metro/index.js +3 -0
- package/metro/with-genie-registry.js +47 -0
- package/package.json +111 -0
- package/scripts/dry-run.js +23 -0
- package/scripts/generate-genie-registry.js +278 -0
- package/scripts/log-file-test.js +51 -0
- package/scripts/parse.js +26 -0
- package/scripts/prompt.js +19 -0
- package/scripts/set-script.js +200 -0
- package/tsconfig.json +36 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const { generateGenieRegistry } = require("../scripts/generate-genie-registry");
|
|
4
|
+
const logger = require("../utils/logger")("genie-registry");
|
|
5
|
+
|
|
6
|
+
function withGenieRegistry(config, options = {}) {
|
|
7
|
+
const projectRoot = options.projectRoot || process.cwd();
|
|
8
|
+
const appDir = options.appDir || path.join(projectRoot, "app");
|
|
9
|
+
const outFile = options.outFile || path.join(appDir, "genie-registry.ts");
|
|
10
|
+
const watch =
|
|
11
|
+
options.watch !== undefined ? options.watch : process.env.NODE_ENV !== "production";
|
|
12
|
+
|
|
13
|
+
generateGenieRegistry({ projectRoot, appDir, outFile });
|
|
14
|
+
|
|
15
|
+
if (watch && fs.existsSync(appDir)) {
|
|
16
|
+
let timer = null;
|
|
17
|
+
const watcher = fs.watch(
|
|
18
|
+
appDir,
|
|
19
|
+
{ recursive: true },
|
|
20
|
+
(eventType, filename) => {
|
|
21
|
+
if (!filename) return;
|
|
22
|
+
if (filename.endsWith("genie-registry.ts")) return;
|
|
23
|
+
if (!filename.endsWith(".ts") && !filename.endsWith(".tsx")) return;
|
|
24
|
+
if (timer) clearTimeout(timer);
|
|
25
|
+
timer = setTimeout(() => {
|
|
26
|
+
try {
|
|
27
|
+
generateGenieRegistry({ projectRoot, appDir, outFile });
|
|
28
|
+
} catch (error) {
|
|
29
|
+
logger.error(String(error));
|
|
30
|
+
}
|
|
31
|
+
}, 100);
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
if (watcher && watcher.on) {
|
|
36
|
+
watcher.on("error", (error) => {
|
|
37
|
+
logger.warn("[genie-registry] watcher error:", error);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return config;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = {
|
|
46
|
+
withGenieRegistry,
|
|
47
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@garrix82/reactgenie-lib",
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "A Toolkit for Multimodal Applications",
|
|
5
|
+
"author": "Omkar Mirgal <github.com/OmkarMirgal>",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"keywords": ["Multimodal", "Voice-interface", "React Native", "Toolkit"],
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "jest",
|
|
11
|
+
"test:babel": "babel-node -x .ts ./scripts/get-prompt.ts",
|
|
12
|
+
"test:watch": "jest --watch",
|
|
13
|
+
"test:coverage": "jest --coverage",
|
|
14
|
+
"test:web": "jest genie-view-wrapper.web.test",
|
|
15
|
+
"test:ios": "jest genie-view-wrapper.ios.test",
|
|
16
|
+
"test:android": "jest genie-view-wrapper.android.test",
|
|
17
|
+
"prepare": "if [ -d \"dist\" ]; then rm -r dist; fi && tsc --emitDeclarationOnly --skipLibCheck && pnpm babel src --out-file-extension .js --out-dir dist --extensions \".ts,.tsx\" --source-maps inline",
|
|
18
|
+
"clean": "rm -rf dist",
|
|
19
|
+
"publish:local": "pnpm publish --registry http://localhost:4873 --no-git-checks",
|
|
20
|
+
"version:patch": "pnpm version patch && pnpm run publish:local"
|
|
21
|
+
},
|
|
22
|
+
"bin": {
|
|
23
|
+
"get-prompt": "./scripts/prompt.js",
|
|
24
|
+
"dry-run": "./scripts/dry-run.js",
|
|
25
|
+
"set-script": "./scripts/set-script.js",
|
|
26
|
+
"parse": "./scripts/parse.js"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@babel/cli": "^7.23.0",
|
|
30
|
+
"@babel/node": "^7.22.19",
|
|
31
|
+
"@babel/preset-env": "^7.28.0",
|
|
32
|
+
"@babel/preset-typescript": "^7.27.1",
|
|
33
|
+
"@testing-library/jest-dom": "^6.1.5",
|
|
34
|
+
"@testing-library/react": "^14.1.2",
|
|
35
|
+
"@testing-library/react-native": "^12.4.2",
|
|
36
|
+
"@types/jest": "^29.5.11",
|
|
37
|
+
"@types/node": "^25.3.0",
|
|
38
|
+
"@types/react-speech-recognition": "^3.9.4",
|
|
39
|
+
"@typescript-eslint/eslint-plugin": "^6.10.0",
|
|
40
|
+
"@typescript-eslint/parser": "^6.10.0",
|
|
41
|
+
"babel-jest": "^29.7.0",
|
|
42
|
+
"babel-plugin-parameter-decorator": "^1.0.16",
|
|
43
|
+
"babel-plugin-reactgenie": "0.3.8",
|
|
44
|
+
"eslint": "^8.53.0",
|
|
45
|
+
"eslint-plugin-react": "^7.32.2",
|
|
46
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
47
|
+
"expo-yarn-workspaces": "^2.2.2",
|
|
48
|
+
"jest": "^29.7.0",
|
|
49
|
+
"jest-environment-jsdom": "^29.7.0",
|
|
50
|
+
"prettier": "3.0.3",
|
|
51
|
+
"react-test-renderer": "^19.2.0"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@babel/core": "^7.23.3",
|
|
55
|
+
"@babel/plugin-proposal-decorators": "^7.23.3",
|
|
56
|
+
"@babel/preset-react": "^7.23.3",
|
|
57
|
+
"@expo/vector-icons": "^13.0.0 || ^14.0.0 || ^15.0.0",
|
|
58
|
+
"@garrix82/reactgenie-dsl": "1.0.0",
|
|
59
|
+
"@react-native-material/core": "^1.3.7",
|
|
60
|
+
"@react-navigation/stack": "^7.0.0",
|
|
61
|
+
"@reduxjs/toolkit": "^2.8.2",
|
|
62
|
+
"expo-blur": "~15.0.8",
|
|
63
|
+
"microsoft-cognitiveservices-speech-sdk": "1.32.0",
|
|
64
|
+
"p-defer-es5": "^2.0.1",
|
|
65
|
+
"react-native-elements": "^3.4.3",
|
|
66
|
+
"react-native-logs": "^5.1.0",
|
|
67
|
+
"react-native-root-toast": "^3.5.1",
|
|
68
|
+
"react-speech-recognition": "^3.9.1",
|
|
69
|
+
"reflect-metadata": "^0.1.13",
|
|
70
|
+
"regenerator-runtime": "^0.14.0",
|
|
71
|
+
"sonner-native": "^0.21.1",
|
|
72
|
+
"web-speech-cognitive-services": "7.1.3"
|
|
73
|
+
},
|
|
74
|
+
"optionalDependencies": {
|
|
75
|
+
"winston": "^3.10.0"
|
|
76
|
+
},
|
|
77
|
+
"peerDependencies": {
|
|
78
|
+
"@react-navigation/native": "^6.1.9 || ^7.0.0",
|
|
79
|
+
"@react-navigation/native-stack": "^6.9.17 || ^7.0.0",
|
|
80
|
+
"expo-audio": "*",
|
|
81
|
+
"expo-file-system": "~17.0.0 || ~18.0.0",
|
|
82
|
+
"expo-router": "^6.0.0",
|
|
83
|
+
"expo-speech": "*",
|
|
84
|
+
"react": "^18.2.0 || ^19.0.0",
|
|
85
|
+
"react-dom": "^18.2.0 || ^19.0.0",
|
|
86
|
+
"react-native": "^0.72.6 || ^0.73.0 || ^0.74.0 || ^0.75.0 || ^0.76.0 || ^0.81.0",
|
|
87
|
+
"react-native-webrtc": "*",
|
|
88
|
+
"react-redux": "^8.1.3 || ^9.0.0",
|
|
89
|
+
"redux": "^4.2.1 || ^5.0.0"
|
|
90
|
+
},
|
|
91
|
+
"peerDependenciesMeta": {
|
|
92
|
+
"expo-router": {
|
|
93
|
+
"optional": true
|
|
94
|
+
},
|
|
95
|
+
"@react-navigation/native-stack": {
|
|
96
|
+
"optional": true
|
|
97
|
+
},
|
|
98
|
+
"expo-audio": {
|
|
99
|
+
"optional": true
|
|
100
|
+
},
|
|
101
|
+
"expo-file-system": {
|
|
102
|
+
"optional": true
|
|
103
|
+
},
|
|
104
|
+
"expo-speech": {
|
|
105
|
+
"optional": true
|
|
106
|
+
},
|
|
107
|
+
"react-native-webrtc": {
|
|
108
|
+
"optional": true
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// const args = process.argv.slice(2);
|
|
4
|
+
// console.log("Input Command:\n", args);
|
|
5
|
+
|
|
6
|
+
// let fs = require("fs");
|
|
7
|
+
// fs.writeFileSync("./__test__/dry-run-input.txt", args[0]);
|
|
8
|
+
|
|
9
|
+
const { spawn } = require("child_process");
|
|
10
|
+
let command = "npx";
|
|
11
|
+
|
|
12
|
+
// Check if the operating system is Windows
|
|
13
|
+
if (process.platform === "win32") {
|
|
14
|
+
command = "npx.cmd";
|
|
15
|
+
}
|
|
16
|
+
const childProcess = spawn(command, ["jest", "./__test__/dry-run.test.ts"]);
|
|
17
|
+
childProcess.stdout.pipe(process.stdout);
|
|
18
|
+
|
|
19
|
+
console.log("Running Test ... ");
|
|
20
|
+
// childProcess.on("close", function(code, signal) {
|
|
21
|
+
// const result = fs.readFileSync("./__test__/dry-run-result.txt", "utf8");
|
|
22
|
+
// console.log("Result:\n", result);
|
|
23
|
+
// });
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
const ROUTE_FILE_EXTS = new Set([".ts", ".tsx"]);
|
|
5
|
+
|
|
6
|
+
const HOOK_REGEX =
|
|
7
|
+
/useRegisterGenieRoute\s*\(\s*(['"`])([^'"`]+)\1\s*(?:,\s*(['"`])([^'"`]+)\3\s*)?\)/g;
|
|
8
|
+
|
|
9
|
+
function walk(dir) {
|
|
10
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
11
|
+
const files = [];
|
|
12
|
+
for (const entry of entries) {
|
|
13
|
+
const fullPath = path.join(dir, entry.name);
|
|
14
|
+
if (entry.isDirectory()) {
|
|
15
|
+
if (entry.name === "node_modules" || entry.name === ".expo") {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
files.push(...walk(fullPath));
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (!ROUTE_FILE_EXTS.has(path.extname(entry.name))) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
files.push(fullPath);
|
|
25
|
+
}
|
|
26
|
+
return files;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isRouteFile(appDir, filePath) {
|
|
30
|
+
const rel = path.relative(appDir, filePath);
|
|
31
|
+
const base = path.basename(filePath);
|
|
32
|
+
if (rel.startsWith("genie-registry.")) return false;
|
|
33
|
+
if (base.startsWith("_") || base.startsWith("+")) return false;
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function normalizeRoutePath(appDir, filePath) {
|
|
38
|
+
const rel = path.relative(appDir, filePath);
|
|
39
|
+
const noExt = rel.replace(path.extname(rel), "");
|
|
40
|
+
const rawSegments = noExt.split(path.sep);
|
|
41
|
+
const segments = [];
|
|
42
|
+
for (const segment of rawSegments) {
|
|
43
|
+
if (!segment) continue;
|
|
44
|
+
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (segment === "index") {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
segments.push(segment);
|
|
51
|
+
}
|
|
52
|
+
return "/" + segments.join("/");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function toImportPath(appDir, filePath) {
|
|
56
|
+
const rel = path.relative(appDir, filePath);
|
|
57
|
+
const noExt = rel.replace(path.extname(rel), "");
|
|
58
|
+
return "./" + noExt.split(path.sep).join("/");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function extractRegistrations(content) {
|
|
62
|
+
// Strip comments so commented-out hooks do not get treated as real registrations.
|
|
63
|
+
// This keeps the generated registry aligned with actual executable route code.
|
|
64
|
+
const source = stripComments(content);
|
|
65
|
+
const results = [];
|
|
66
|
+
let match;
|
|
67
|
+
while ((match = HOOK_REGEX.exec(source)) !== null) {
|
|
68
|
+
const viewClassName = match[2];
|
|
69
|
+
const explicitPath = match[4] || null;
|
|
70
|
+
results.push({ viewClassName, explicitPath });
|
|
71
|
+
}
|
|
72
|
+
return results;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function stripComments(input) {
|
|
76
|
+
let output = "";
|
|
77
|
+
let i = 0;
|
|
78
|
+
let inLineComment = false;
|
|
79
|
+
let inBlockComment = false;
|
|
80
|
+
let inSingleQuote = false;
|
|
81
|
+
let inDoubleQuote = false;
|
|
82
|
+
let inTemplateString = false;
|
|
83
|
+
let escaped = false;
|
|
84
|
+
|
|
85
|
+
while (i < input.length) {
|
|
86
|
+
const char = input[i];
|
|
87
|
+
const next = input[i + 1];
|
|
88
|
+
|
|
89
|
+
if (inLineComment) {
|
|
90
|
+
if (char === "\n") {
|
|
91
|
+
inLineComment = false;
|
|
92
|
+
output += char;
|
|
93
|
+
}
|
|
94
|
+
i += 1;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (inBlockComment) {
|
|
99
|
+
if (char === "*" && next === "/") {
|
|
100
|
+
inBlockComment = false;
|
|
101
|
+
i += 2;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (char === "\n") {
|
|
105
|
+
output += char;
|
|
106
|
+
}
|
|
107
|
+
i += 1;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (inSingleQuote) {
|
|
112
|
+
output += char;
|
|
113
|
+
if (!escaped && char === "'") {
|
|
114
|
+
inSingleQuote = false;
|
|
115
|
+
}
|
|
116
|
+
escaped = !escaped && char === "\\";
|
|
117
|
+
i += 1;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (inDoubleQuote) {
|
|
122
|
+
output += char;
|
|
123
|
+
if (!escaped && char === "\"") {
|
|
124
|
+
inDoubleQuote = false;
|
|
125
|
+
}
|
|
126
|
+
escaped = !escaped && char === "\\";
|
|
127
|
+
i += 1;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (inTemplateString) {
|
|
132
|
+
output += char;
|
|
133
|
+
if (!escaped && char === "`") {
|
|
134
|
+
inTemplateString = false;
|
|
135
|
+
}
|
|
136
|
+
escaped = !escaped && char === "\\";
|
|
137
|
+
i += 1;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (char === "/" && next === "/") {
|
|
142
|
+
inLineComment = true;
|
|
143
|
+
i += 2;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (char === "/" && next === "*") {
|
|
148
|
+
inBlockComment = true;
|
|
149
|
+
i += 2;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (char === "'") {
|
|
154
|
+
inSingleQuote = true;
|
|
155
|
+
escaped = false;
|
|
156
|
+
output += char;
|
|
157
|
+
i += 1;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (char === "\"") {
|
|
162
|
+
inDoubleQuote = true;
|
|
163
|
+
escaped = false;
|
|
164
|
+
output += char;
|
|
165
|
+
i += 1;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (char === "`") {
|
|
170
|
+
inTemplateString = true;
|
|
171
|
+
escaped = false;
|
|
172
|
+
output += char;
|
|
173
|
+
i += 1;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
output += char;
|
|
178
|
+
i += 1;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return output;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function generateGenieRegistry(options = {}) {
|
|
185
|
+
const projectRoot = options.projectRoot || process.cwd();
|
|
186
|
+
const appDir =
|
|
187
|
+
options.appDir || process.env.GENIE_APP_DIR || path.join(projectRoot, "app");
|
|
188
|
+
const outFile =
|
|
189
|
+
options.outFile ||
|
|
190
|
+
process.env.GENIE_REGISTRY_OUT ||
|
|
191
|
+
path.join(appDir, "genie-registry.ts");
|
|
192
|
+
|
|
193
|
+
if (!fs.existsSync(appDir)) {
|
|
194
|
+
console.warn(`[genie-registry] app directory not found: ${appDir}`);
|
|
195
|
+
return { appDir, outFile, registrations: [], written: false };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const files = walk(appDir).filter((filePath) => isRouteFile(appDir, filePath));
|
|
199
|
+
const routeImports = new Set();
|
|
200
|
+
const registrations = [];
|
|
201
|
+
const seenViewClass = new Map();
|
|
202
|
+
const conflicts = [];
|
|
203
|
+
|
|
204
|
+
for (const filePath of files) {
|
|
205
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
206
|
+
const hooks = extractRegistrations(content);
|
|
207
|
+
if (hooks.length === 0) continue;
|
|
208
|
+
|
|
209
|
+
const routePath = normalizeRoutePath(appDir, filePath);
|
|
210
|
+
const importPath = toImportPath(appDir, filePath);
|
|
211
|
+
routeImports.add(importPath);
|
|
212
|
+
|
|
213
|
+
for (const hook of hooks) {
|
|
214
|
+
const viewClassName = hook.viewClassName;
|
|
215
|
+
const finalPath = hook.explicitPath || routePath || "/";
|
|
216
|
+
if (seenViewClass.has(viewClassName)) {
|
|
217
|
+
const existing = seenViewClass.get(viewClassName);
|
|
218
|
+
if (existing !== finalPath) {
|
|
219
|
+
conflicts.push(
|
|
220
|
+
`Duplicate view class "${viewClassName}" mapped to "${existing}" and "${finalPath}"`
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
seenViewClass.set(viewClassName, finalPath);
|
|
226
|
+
registrations.push({ viewClassName, routePath: finalPath });
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (conflicts.length > 0) {
|
|
231
|
+
const message = ["[genie-registry] Conflicts found:"]
|
|
232
|
+
.concat(conflicts.map((conflict) => ` - ${conflict}`))
|
|
233
|
+
.join("\n");
|
|
234
|
+
throw new Error(message);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
registrations.sort((a, b) => a.viewClassName.localeCompare(b.viewClassName));
|
|
238
|
+
|
|
239
|
+
const lines = [];
|
|
240
|
+
lines.push("/*");
|
|
241
|
+
lines.push(" * Auto-generated by reactgenie.");
|
|
242
|
+
lines.push(" * Do not edit manually.");
|
|
243
|
+
lines.push(" */");
|
|
244
|
+
lines.push("");
|
|
245
|
+
lines.push('import { registerGenieRoute } from "@garrix82/reactgenie-lib";');
|
|
246
|
+
for (const importPath of Array.from(routeImports).sort()) {
|
|
247
|
+
lines.push(`import "${importPath}";`);
|
|
248
|
+
}
|
|
249
|
+
lines.push("");
|
|
250
|
+
for (const entry of registrations) {
|
|
251
|
+
lines.push(
|
|
252
|
+
`registerGenieRoute("${entry.viewClassName}", "${entry.routePath}");`
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
lines.push("");
|
|
256
|
+
lines.push("// Expo Router requires a default export for every route module.");
|
|
257
|
+
lines.push("export default function GenieRegistryRoute() {");
|
|
258
|
+
lines.push(" return null;");
|
|
259
|
+
lines.push("}");
|
|
260
|
+
lines.push("");
|
|
261
|
+
|
|
262
|
+
fs.mkdirSync(path.dirname(outFile), { recursive: true });
|
|
263
|
+
fs.writeFileSync(outFile, lines.join("\n"));
|
|
264
|
+
return { appDir, outFile, registrations, written: true };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (require.main === module) {
|
|
268
|
+
const result = generateGenieRegistry();
|
|
269
|
+
if (result.written && result.outFile) {
|
|
270
|
+
console.log(
|
|
271
|
+
`[genie-registry] Wrote ${path.relative(process.cwd(), result.outFile)}`
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
module.exports = {
|
|
277
|
+
generateGenieRegistry,
|
|
278
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Simple test script to verify log file creation using the same naming scheme
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const env = process.env;
|
|
7
|
+
const logFileDir = env.REACT_APP_LOG_FILE_DIR || env.EXPO_PUBLIC_LOG_FILE_DIR || env.LOG_FILE_DIR || '.';
|
|
8
|
+
const resolvedDir = path.resolve(process.cwd(), logFileDir);
|
|
9
|
+
|
|
10
|
+
// Helper to build date-based file path
|
|
11
|
+
function buildFilePath() {
|
|
12
|
+
const now = new Date();
|
|
13
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
14
|
+
const dateStamp = `${now.getFullYear()}-${pad(now.getMonth()+1)}-${pad(now.getDate())}`;
|
|
15
|
+
const fileName = `logs-${dateStamp}.log`;
|
|
16
|
+
return path.join(resolvedDir, fileName);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Try to load the real logger from dist or src
|
|
20
|
+
let usedLogger = null;
|
|
21
|
+
try {
|
|
22
|
+
// prefer built dist logger
|
|
23
|
+
const distPath = path.resolve(process.cwd(), 'dist', 'logger.js');
|
|
24
|
+
if (fs.existsSync(distPath)) {
|
|
25
|
+
usedLogger = require(distPath).default || require(distPath).logger || require(distPath);
|
|
26
|
+
}
|
|
27
|
+
} catch (e) { /* ignore */ }
|
|
28
|
+
|
|
29
|
+
if (!usedLogger) {
|
|
30
|
+
try {
|
|
31
|
+
// try to require source logger (may fail if no ts support)
|
|
32
|
+
const srcPath = path.resolve(process.cwd(), 'src', 'logger.ts');
|
|
33
|
+
if (fs.existsSync(srcPath)) {
|
|
34
|
+
usedLogger = require(srcPath).default || require(srcPath).logger || require(srcPath);
|
|
35
|
+
}
|
|
36
|
+
} catch (e) { /* ignore */ }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (usedLogger && typeof usedLogger.info === 'function') {
|
|
40
|
+
usedLogger.info('log-file-test: writing test log via logger');
|
|
41
|
+
console.log('Wrote test log via logger (check configured log outputs)');
|
|
42
|
+
} else {
|
|
43
|
+
// fallback: ensure dir exists and append file directly
|
|
44
|
+
if (!fs.existsSync(resolvedDir)) {
|
|
45
|
+
try { fs.mkdirSync(resolvedDir, { recursive: true }); } catch (e) { console.error('mkdir failed', e); process.exit(1); }
|
|
46
|
+
}
|
|
47
|
+
const filePath = buildFilePath();
|
|
48
|
+
const line = `[test] ${new Date().toISOString()} test log line\n`;
|
|
49
|
+
fs.appendFileSync(filePath, line);
|
|
50
|
+
console.log('Wrote fallback test log to', filePath);
|
|
51
|
+
}
|
package/scripts/parse.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const args = process.argv.slice(2);
|
|
4
|
+
console.log("Input Command:\n", args);
|
|
5
|
+
|
|
6
|
+
// let fs = require("fs");
|
|
7
|
+
// fs.writeFileSync("./__test__/dry-run-input.txt", args[0]);
|
|
8
|
+
|
|
9
|
+
const { spawn } = require("child_process");
|
|
10
|
+
let command = "npx";
|
|
11
|
+
|
|
12
|
+
// Check if the operating system is Windows
|
|
13
|
+
if (process.platform === "win32") {
|
|
14
|
+
command = "npx.cmd";
|
|
15
|
+
}
|
|
16
|
+
const childProcess = spawn(command, ["jest", "./__test__/parse.test.ts", args[0]]);
|
|
17
|
+
console.log("Running Test ... ");
|
|
18
|
+
childProcess.stdout.pipe(process.stdout);
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
// console.log("Running Test ... ");
|
|
23
|
+
// childProcess.on("close", function(code, signal) {
|
|
24
|
+
// const result = fs.readFileSync("./__test__/dry-run-result.txt", "utf8");
|
|
25
|
+
// console.log("Result:\n", result);
|
|
26
|
+
// });
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawn } = require("child_process");
|
|
4
|
+
|
|
5
|
+
let command = "npx";
|
|
6
|
+
|
|
7
|
+
// Check if the operating system is Windows
|
|
8
|
+
if (process.platform === "win32") {
|
|
9
|
+
command = "npx.cmd";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const childProcess = spawn(command, ["jest", "./__test__/prompt.test.ts"]);
|
|
13
|
+
console.log("Generating Prompt ... ");
|
|
14
|
+
|
|
15
|
+
let fs = require("fs");
|
|
16
|
+
childProcess.on("exit", function (code, signal) {
|
|
17
|
+
const result = fs.readFileSync("./__test__/prompt.txt", "utf8");
|
|
18
|
+
console.log("Prompt:\n", result);
|
|
19
|
+
});
|