otter_pm/
types.rs

1//! Bundled TypeScript type definitions management
2//!
3//! This module handles copying Otter's bundled type definitions to node_modules/@types
4//! so that editors can provide IntelliSense and go-to-definition support.
5
6use std::fs;
7use std::path::Path;
8
9/// Otter global API types (console, timers, fetch, etc.)
10const OTTER_TYPES: &str = include_str!("types/otter.d.ts");
11
12/// Node.js Buffer types
13const NODE_BUFFER_TYPES: &str = include_str!("types/node/buffer.d.ts");
14
15/// Node.js fs types
16const NODE_FS_TYPES: &str = include_str!("types/node/fs.d.ts");
17
18/// Node.js path types
19const NODE_PATH_TYPES: &str = include_str!("types/node/path.d.ts");
20
21/// Node.js test types
22const NODE_TEST_TYPES: &str = include_str!("types/node/test.d.ts");
23
24/// Install bundled type definitions to node_modules
25pub fn install_bundled_types(node_modules: &Path) -> Result<(), TypesError> {
26    // Install @types/otter for global APIs
27    install_otter_types(node_modules)?;
28
29    // Install @types/node for Node.js compatibility APIs
30    install_node_types(node_modules)?;
31
32    Ok(())
33}
34
35/// Install @types/otter (global Otter APIs)
36fn install_otter_types(node_modules: &Path) -> Result<(), TypesError> {
37    let types_dir = node_modules.join("@types").join("otter");
38    fs::create_dir_all(&types_dir).map_err(|e| TypesError::Io(e.to_string()))?;
39
40    // Write index.d.ts
41    fs::write(types_dir.join("index.d.ts"), OTTER_TYPES)
42        .map_err(|e| TypesError::Io(e.to_string()))?;
43
44    // Write package.json
45    let package_json = r#"{
46  "name": "@types/otter",
47  "version": "0.1.0",
48  "description": "TypeScript definitions for Otter runtime",
49  "types": "index.d.ts",
50  "license": "MIT"
51}"#;
52    fs::write(types_dir.join("package.json"), package_json)
53        .map_err(|e| TypesError::Io(e.to_string()))?;
54
55    Ok(())
56}
57
58/// Install @types/node (Node.js compatibility APIs)
59fn install_node_types(node_modules: &Path) -> Result<(), TypesError> {
60    let types_dir = node_modules.join("@types").join("node");
61    fs::create_dir_all(&types_dir).map_err(|e| TypesError::Io(e.to_string()))?;
62
63    // Write individual module types
64    fs::write(types_dir.join("buffer.d.ts"), NODE_BUFFER_TYPES)
65        .map_err(|e| TypesError::Io(e.to_string()))?;
66
67    fs::write(types_dir.join("fs.d.ts"), NODE_FS_TYPES)
68        .map_err(|e| TypesError::Io(e.to_string()))?;
69
70    fs::write(types_dir.join("path.d.ts"), NODE_PATH_TYPES)
71        .map_err(|e| TypesError::Io(e.to_string()))?;
72
73    fs::write(types_dir.join("test.d.ts"), NODE_TEST_TYPES)
74        .map_err(|e| TypesError::Io(e.to_string()))?;
75
76    // Write index.d.ts that re-exports all modules
77    let index_dts = r#"/// <reference path="buffer.d.ts" />
78/// <reference path="fs.d.ts" />
79/// <reference path="path.d.ts" />
80/// <reference path="test.d.ts" />
81
82// Re-export modules for direct imports
83export * from "node:buffer";
84export * from "node:fs";
85export * from "node:path";
86export * from "node:test";
87"#;
88    fs::write(types_dir.join("index.d.ts"), index_dts)
89        .map_err(|e| TypesError::Io(e.to_string()))?;
90
91    // Write package.json
92    let package_json = r#"{
93  "name": "@types/node",
94  "version": "0.1.0",
95  "description": "TypeScript definitions for Otter's Node.js compatibility layer",
96  "types": "index.d.ts",
97  "license": "MIT"
98}"#;
99    fs::write(types_dir.join("package.json"), package_json)
100        .map_err(|e| TypesError::Io(e.to_string()))?;
101
102    Ok(())
103}
104
105#[derive(Debug, thiserror::Error)]
106pub enum TypesError {
107    #[error("IO error: {0}")]
108    Io(String),
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn test_bundled_types_not_empty() {
117        assert!(!OTTER_TYPES.is_empty());
118        assert!(!NODE_BUFFER_TYPES.is_empty());
119        assert!(!NODE_FS_TYPES.is_empty());
120        assert!(!NODE_PATH_TYPES.is_empty());
121    }
122
123    #[test]
124    fn test_install_bundled_types() {
125        let temp_dir = std::env::temp_dir().join("otter-types-test");
126        let node_modules = temp_dir.join("node_modules");
127
128        // Clean up first
129        let _ = fs::remove_dir_all(&temp_dir);
130        fs::create_dir_all(&node_modules).unwrap();
131
132        // Install types
133        install_bundled_types(&node_modules).unwrap();
134
135        // Verify @types/otter
136        assert!(node_modules.join("@types/otter/index.d.ts").exists());
137        assert!(node_modules.join("@types/otter/package.json").exists());
138
139        // Verify @types/node
140        assert!(node_modules.join("@types/node/index.d.ts").exists());
141        assert!(node_modules.join("@types/node/fs.d.ts").exists());
142        assert!(node_modules.join("@types/node/buffer.d.ts").exists());
143        assert!(node_modules.join("@types/node/path.d.ts").exists());
144
145        // Clean up
146        let _ = fs::remove_dir_all(&temp_dir);
147    }
148}