|
1 | 1 | // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
2 | 2 |
|
3 |
| -use std::path::Path; |
4 |
| -use std::path::PathBuf; |
5 |
| - |
6 |
| -mod test_builder; |
7 |
| - |
8 |
| -use deno_graph::ModuleSpecifier; |
9 |
| -use indexmap::IndexMap; |
10 |
| -use serde::Deserialize; |
11 |
| -use serde::Serialize; |
12 |
| -pub use test_builder::*; |
13 |
| - |
14 |
| -pub struct Spec { |
15 |
| - pub private: bool, |
16 |
| - pub files: Vec<SpecFile>, |
17 |
| - pub output_json_file: SpecFile, |
18 |
| - pub output_doc_file: SpecFile, |
19 |
| - pub diagnostics: String, |
| 3 | +use deno_ast::ModuleSpecifier; |
| 4 | +use deno_doc::DocDiagnostic; |
| 5 | +use deno_doc::DocParser; |
| 6 | +use deno_doc::DocParserOptions; |
| 7 | +use deno_doc::DocPrinter; |
| 8 | +use deno_graph::source::MemoryLoader; |
| 9 | +use deno_graph::BuildOptions; |
| 10 | +use deno_graph::CapturingModuleAnalyzer; |
| 11 | +use deno_graph::GraphKind; |
| 12 | + |
| 13 | +pub struct BuildResult { |
| 14 | + pub json_output: serde_json::Value, |
| 15 | + pub text_output: String, |
| 16 | + pub diagnostics: Vec<DocDiagnostic>, |
20 | 17 | }
|
21 | 18 |
|
22 |
| -impl Spec { |
23 |
| - pub fn emit(&self) -> String { |
24 |
| - let mut text = String::new(); |
25 |
| - if self.private { |
26 |
| - text.push_str("{ \"private\": true }\n"); |
27 |
| - } |
28 |
| - for file in &self.files { |
29 |
| - text.push_str(&file.emit()); |
30 |
| - text.push('\n'); |
31 |
| - } |
32 |
| - if !self.diagnostics.is_empty() { |
33 |
| - text.push_str("# diagnostics\n"); |
34 |
| - text.push_str(&self.diagnostics); |
35 |
| - text.push('\n'); |
36 |
| - } |
37 |
| - text.push_str(&self.output_doc_file.emit()); |
38 |
| - text.push('\n'); |
39 |
| - text.push_str(&self.output_json_file.emit()); |
40 |
| - text |
41 |
| - } |
| 19 | +pub struct TestBuilder { |
| 20 | + loader: MemoryLoader, |
| 21 | + private: bool, |
| 22 | + entry_point: String, |
42 | 23 | }
|
43 | 24 |
|
44 |
| -#[derive(Debug, Serialize, Deserialize)] |
45 |
| -struct SpecOptions { |
46 |
| - pub private: bool, |
47 |
| -} |
48 |
| - |
49 |
| -#[derive(Debug)] |
50 |
| -pub struct SpecFile { |
51 |
| - pub specifier: String, |
52 |
| - pub text: String, |
53 |
| - pub headers: IndexMap<String, String>, |
54 |
| -} |
55 |
| - |
56 |
| -impl SpecFile { |
57 |
| - pub fn emit(&self) -> String { |
58 |
| - let mut text = format!("# {}\n", self.specifier); |
59 |
| - if !self.headers.is_empty() { |
60 |
| - text.push_str(&format!( |
61 |
| - "HEADERS: {}\n", |
62 |
| - serde_json::to_string(&self.headers).unwrap() |
63 |
| - )); |
64 |
| - } |
65 |
| - text.push_str(&self.text); |
66 |
| - text |
67 |
| - } |
68 |
| - |
69 |
| - pub fn url(&self) -> ModuleSpecifier { |
70 |
| - let specifier = &self.specifier; |
71 |
| - if !specifier.starts_with("http") && !specifier.starts_with("file") { |
72 |
| - ModuleSpecifier::parse(&format!("file:///{}", specifier)).unwrap() |
73 |
| - } else { |
74 |
| - ModuleSpecifier::parse(specifier).unwrap() |
| 25 | +impl TestBuilder { |
| 26 | + pub fn new() -> Self { |
| 27 | + Self { |
| 28 | + private: false, |
| 29 | + loader: Default::default(), |
| 30 | + entry_point: "file:///mod.ts".to_string(), |
75 | 31 | }
|
76 | 32 | }
|
77 |
| -} |
78 |
| - |
79 |
| -pub fn get_specs_in_dir(path: &Path) -> Vec<(PathBuf, Spec)> { |
80 |
| - let files = collect_files_in_dir_recursive(path); |
81 |
| - let files = if files |
82 |
| - .iter() |
83 |
| - .any(|file| file.path.to_string_lossy().to_lowercase().contains("_only")) |
84 |
| - { |
85 |
| - files |
86 |
| - .into_iter() |
87 |
| - .filter(|file| { |
88 |
| - file.path.to_string_lossy().to_lowercase().contains("_only") |
89 |
| - }) |
90 |
| - .collect() |
91 |
| - } else { |
92 |
| - files |
93 |
| - }; |
94 |
| - files |
95 |
| - .into_iter() |
96 |
| - .map(|file| (file.path, parse_spec(file.text))) |
97 |
| - .collect() |
98 |
| -} |
99 | 33 |
|
100 |
| -fn parse_spec(text: String) -> Spec { |
101 |
| - let mut files = Vec::new(); |
102 |
| - let mut current_file = None; |
103 |
| - let mut options: Option<SpecOptions> = None; |
104 |
| - for (i, line) in text.split('\n').enumerate() { |
105 |
| - if i == 0 && line.starts_with('{') { |
106 |
| - options = Some(serde_json::from_str(line).unwrap()); |
107 |
| - continue; |
108 |
| - } |
109 |
| - if let Some(specifier) = line.strip_prefix("# ") { |
110 |
| - if let Some(file) = current_file.take() { |
111 |
| - files.push(file); |
112 |
| - } |
113 |
| - current_file = Some(SpecFile { |
114 |
| - specifier: specifier.to_string(), |
115 |
| - text: String::new(), |
116 |
| - headers: Default::default(), |
117 |
| - }); |
118 |
| - } else if let Some(headers) = line.strip_prefix("HEADERS: ") { |
119 |
| - current_file.as_mut().unwrap().headers = |
120 |
| - serde_json::from_str(headers).unwrap(); |
121 |
| - } else { |
122 |
| - let current_file = current_file.as_mut().unwrap(); |
123 |
| - if !current_file.text.is_empty() { |
124 |
| - current_file.text.push('\n'); |
125 |
| - } |
126 |
| - current_file.text.push_str(line); |
127 |
| - } |
128 |
| - } |
129 |
| - files.push(current_file.unwrap()); |
130 |
| - let output_json_file = files.remove( |
131 |
| - files |
132 |
| - .iter() |
133 |
| - .position(|f| f.specifier == "output.json") |
134 |
| - .unwrap(), |
135 |
| - ); |
136 |
| - let output_doc_file = files.remove( |
137 |
| - files |
138 |
| - .iter() |
139 |
| - .position(|f| f.specifier == "output.txt") |
140 |
| - .unwrap(), |
141 |
| - ); |
142 |
| - let diagnostics = take_text_file(&mut files, "diagnostics"); |
143 |
| - Spec { |
144 |
| - private: options.map(|o| o.private).unwrap_or(false), |
145 |
| - files, |
146 |
| - output_json_file, |
147 |
| - output_doc_file, |
148 |
| - diagnostics, |
| 34 | + pub fn with_loader( |
| 35 | + &mut self, |
| 36 | + mut action: impl FnMut(&mut MemoryLoader), |
| 37 | + ) -> &mut Self { |
| 38 | + action(&mut self.loader); |
| 39 | + self |
149 | 40 | }
|
150 |
| -} |
151 | 41 |
|
152 |
| -fn take_text_file(files: &mut Vec<SpecFile>, name: &str) -> String { |
153 |
| - if let Some(index) = files.iter().position(|f| f.specifier == name) { |
154 |
| - let file = files.remove(index); |
155 |
| - file.text |
156 |
| - } else { |
157 |
| - String::new() |
| 42 | + pub fn set_private(&mut self, value: bool) -> &mut Self { |
| 43 | + self.private = value; |
| 44 | + self |
158 | 45 | }
|
159 |
| -} |
160 |
| - |
161 |
| -struct CollectedFile { |
162 |
| - pub path: PathBuf, |
163 |
| - pub text: String, |
164 |
| -} |
165 |
| - |
166 |
| -fn collect_files_in_dir_recursive(path: &Path) -> Vec<CollectedFile> { |
167 |
| - let mut result = Vec::new(); |
168 | 46 |
|
169 |
| - for entry in path.read_dir().unwrap().flatten() { |
170 |
| - let entry_path = entry.path(); |
171 |
| - if entry_path.is_file() { |
172 |
| - let text = std::fs::read_to_string(&entry_path).unwrap(); |
173 |
| - result.push(CollectedFile { |
174 |
| - path: entry_path, |
175 |
| - text, |
176 |
| - }); |
177 |
| - } else { |
178 |
| - result.extend(collect_files_in_dir_recursive(&entry_path)); |
| 47 | + pub async fn build(&mut self) -> BuildResult { |
| 48 | + let analyzer = CapturingModuleAnalyzer::default(); |
| 49 | + let mut graph = deno_graph::ModuleGraph::new(GraphKind::TypesOnly); |
| 50 | + let entry_point_url = ModuleSpecifier::parse(&self.entry_point).unwrap(); |
| 51 | + let roots = vec![entry_point_url.clone()]; |
| 52 | + graph |
| 53 | + .build( |
| 54 | + roots.clone(), |
| 55 | + &mut self.loader, |
| 56 | + BuildOptions { |
| 57 | + module_analyzer: &analyzer, |
| 58 | + ..Default::default() |
| 59 | + }, |
| 60 | + ) |
| 61 | + .await; |
| 62 | + graph.valid().unwrap(); |
| 63 | + let parser = DocParser::new( |
| 64 | + &graph, |
| 65 | + &analyzer, |
| 66 | + DocParserOptions { |
| 67 | + private: self.private, |
| 68 | + diagnostics: true, |
| 69 | + }, |
| 70 | + ) |
| 71 | + .unwrap(); |
| 72 | + let entries = parser.parse_with_reexports(&entry_point_url).unwrap(); |
| 73 | + |
| 74 | + let doc = DocPrinter::new(&entries, false, self.private).to_string(); |
| 75 | + let diagnostics = parser.take_diagnostics(); |
| 76 | + |
| 77 | + BuildResult { |
| 78 | + diagnostics, |
| 79 | + json_output: serde_json::to_value(entries).unwrap(), |
| 80 | + text_output: doc, |
179 | 81 | }
|
180 | 82 | }
|
181 |
| - |
182 |
| - result |
183 | 83 | }
|
0 commit comments