Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

gleam / compiler-core / src / javascript / import.rs
6.4 kB 250 lines
1use std::collections::{HashMap, HashSet}; 2 3use ecow::EcoString; 4use itertools::Itertools; 5 6use crate::{ 7 docvec, 8 javascript::{INDENT, JavaScriptCodegenTarget}, 9 pretty::{Document, Documentable, break_, concat, join, line}, 10}; 11 12/// A collection of JavaScript import statements from Gleam imports and from 13/// external functions, to be rendered into a JavaScript module. 14/// 15#[derive(Debug, Default)] 16pub(crate) struct Imports<'a> { 17 imports: HashMap<EcoString, Import<'a>>, 18 exports: HashSet<EcoString>, 19} 20 21impl<'a> Imports<'a> { 22 pub fn new() -> Self { 23 Self::default() 24 } 25 26 pub fn register_export(&mut self, export: EcoString) { 27 let _ = self.exports.insert(export); 28 } 29 30 pub fn register_module( 31 &mut self, 32 path: EcoString, 33 aliases: impl IntoIterator<Item = EcoString>, 34 unqualified_imports: impl IntoIterator<Item = Member<'a>>, 35 ) { 36 let import = self 37 .imports 38 .entry(path.clone()) 39 .or_insert_with(|| Import::new(path.clone())); 40 import.aliases.extend(aliases); 41 import.unqualified.extend(unqualified_imports) 42 } 43 44 pub fn into_doc(self, codegen_target: JavaScriptCodegenTarget) -> Document<'a> { 45 let imports = concat( 46 self.imports 47 .into_values() 48 .sorted_by(|a, b| a.path.cmp(&b.path)) 49 .map(|import| Import::into_doc(import, codegen_target)), 50 ); 51 52 if self.exports.is_empty() { 53 imports 54 } else { 55 let names = join( 56 self.exports 57 .into_iter() 58 .sorted() 59 .map(|string| string.to_doc()), 60 break_(",", ", "), 61 ); 62 let names = docvec![ 63 docvec![break_("", " "), names].nest(INDENT), 64 break_(",", " ") 65 ] 66 .group(); 67 68 let export_keyword = match codegen_target { 69 JavaScriptCodegenTarget::JavaScript => "export {", 70 JavaScriptCodegenTarget::TypeScriptDeclarations => "export type {", 71 }; 72 73 imports 74 .append(line()) 75 .append(export_keyword) 76 .append(names) 77 .append("};") 78 .append(line()) 79 } 80 } 81 82 pub fn is_empty(&self) -> bool { 83 self.imports.is_empty() && self.exports.is_empty() 84 } 85} 86 87#[derive(Debug)] 88struct Import<'a> { 89 path: EcoString, 90 aliases: HashSet<EcoString>, 91 unqualified: Vec<Member<'a>>, 92} 93 94impl<'a> Import<'a> { 95 fn new(path: EcoString) -> Self { 96 Self { 97 path, 98 aliases: Default::default(), 99 unqualified: Default::default(), 100 } 101 } 102 103 pub fn into_doc(self, codegen_target: JavaScriptCodegenTarget) -> Document<'a> { 104 let path = self.path.to_doc(); 105 let import_modifier = if codegen_target == JavaScriptCodegenTarget::TypeScriptDeclarations { 106 "type " 107 } else { 108 "" 109 }; 110 let alias_imports = concat(self.aliases.into_iter().sorted().map(|alias| { 111 docvec![ 112 "import ", 113 import_modifier, 114 "* as ", 115 alias, 116 " from \"", 117 path.clone(), 118 r#"";"#, 119 line() 120 ] 121 })); 122 if self.unqualified.is_empty() { 123 alias_imports 124 } else { 125 let members = self.unqualified.into_iter().map(Member::into_doc); 126 let members = join(members, break_(",", ", ")); 127 let members = docvec![ 128 docvec![break_("", " "), members].nest(INDENT), 129 break_(",", " ") 130 ] 131 .group(); 132 docvec![ 133 alias_imports, 134 "import ", 135 import_modifier, 136 "{", 137 members, 138 "} from \"", 139 path, 140 r#"";"#, 141 line() 142 ] 143 } 144 } 145} 146 147#[derive(Debug)] 148pub struct Member<'a> { 149 pub name: Document<'a>, 150 pub alias: Option<Document<'a>>, 151} 152 153impl<'a> Member<'a> { 154 fn into_doc(self) -> Document<'a> { 155 match self.alias { 156 None => self.name, 157 Some(alias) => docvec![self.name, " as ", alias], 158 } 159 } 160} 161 162#[test] 163fn into_doc() { 164 let mut imports = Imports::new(); 165 imports.register_module("./gleam/empty".into(), [], []); 166 imports.register_module( 167 "./multiple/times".into(), 168 ["wibble".into(), "wobble".into()], 169 [], 170 ); 171 imports.register_module("./multiple/times".into(), ["wubble".into()], []); 172 imports.register_module( 173 "./multiple/times".into(), 174 [], 175 [Member { 176 name: "one".to_doc(), 177 alias: None, 178 }], 179 ); 180 181 imports.register_module( 182 "./other".into(), 183 [], 184 [ 185 Member { 186 name: "one".to_doc(), 187 alias: None, 188 }, 189 Member { 190 name: "one".to_doc(), 191 alias: Some("onee".to_doc()), 192 }, 193 Member { 194 name: "two".to_doc(), 195 alias: Some("twoo".to_doc()), 196 }, 197 ], 198 ); 199 200 imports.register_module( 201 "./other".into(), 202 [], 203 [ 204 Member { 205 name: "three".to_doc(), 206 alias: None, 207 }, 208 Member { 209 name: "four".to_doc(), 210 alias: None, 211 }, 212 ], 213 ); 214 215 imports.register_module( 216 "./zzz".into(), 217 [], 218 [ 219 Member { 220 name: "one".to_doc(), 221 alias: None, 222 }, 223 Member { 224 name: "two".to_doc(), 225 alias: None, 226 }, 227 ], 228 ); 229 230 assert_eq!( 231 line() 232 .append(imports.into_doc(JavaScriptCodegenTarget::JavaScript)) 233 .to_pretty_string(40), 234 r#" 235import * as wibble from "./multiple/times"; 236import * as wobble from "./multiple/times"; 237import * as wubble from "./multiple/times"; 238import { one } from "./multiple/times"; 239import { 240 one, 241 one as onee, 242 two as twoo, 243 three, 244 four, 245} from "./other"; 246import { one, two } from "./zzz"; 247"# 248 .to_string() 249 ); 250}