1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use farmfe_macro_cache_item::cache_item;
use crate::module::ModuleId;
use crate::HashMap;
#[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default)]
#[serde(rename_all = "camelCase")]
#[cache_item]
pub enum ResolveKind {
/// entry input in the config
Entry(String),
/// dynamic entry input, which will always be bundled as a separate resource
/// all deep dependencies of a dynamic entry will be merged into a special module group
DynamicEntry {
name: String,
/// the same as config.output.filename, default to config.output.filename
output_filename: Option<String>,
},
/// static import, e.g. `import a from './a'`
#[default]
Import,
/// static export, e.g. `export * from './a'`
ExportFrom,
/// dynamic import, e.g. `import('./a').then(module => console.log(module))`
DynamicImport,
/// cjs require, e.g. `require('./a')`
Require,
/// @import of css, e.g. @import './a.css'
CssAtImport,
/// url() of css, e.g. url('./a.png')
CssUrl,
/// `<script src="./index.html" />` of html
ScriptSrc,
/// `<link href="index.css" />` of html
LinkHref,
/// Hmr update
HmrUpdate,
/// Custom ResolveKind
Custom(String),
}
impl ResolveKind {
/// dynamic if self is [ResolveKind::DynamicImport] or [ResolveKind::Custom("dynamic:xxx")] (dynamic means the module is loaded dynamically, for example, fetch from network)
/// used when analyzing module groups
pub fn is_dynamic_import(&self) -> bool {
matches!(self, ResolveKind::DynamicImport)
|| matches!(self, ResolveKind::Custom(c) if c.starts_with("dynamic:"))
}
pub fn is_dynamic_entry(&self) -> bool {
matches!(self, ResolveKind::DynamicEntry { .. })
}
pub fn is_export_from(&self) -> bool {
matches!(self, ResolveKind::ExportFrom)
}
pub fn is_require(&self) -> bool {
matches!(self, ResolveKind::Require)
}
}
impl From<&str> for ResolveKind {
fn from(value: &str) -> Self {
serde_json::from_str(value).unwrap()
}
}
impl From<ResolveKind> for String {
fn from(value: ResolveKind) -> Self {
serde_json::to_string(&value).unwrap()
}
}
/// Parameter of the resolve hook
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Hash, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct PluginResolveHookParam {
/// the source would like to resolve, for example, './index'
pub source: String,
/// the start location to resolve `specifier`, being [None] if resolving a entry or resolving a hmr update.
pub importer: Option<ModuleId>,
/// for example, [ResolveKind::Import] for static import (`import a from './a'`)
pub kind: ResolveKind,
}
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)]
#[serde(rename_all = "camelCase", default)]
pub struct PluginResolveHookResult {
/// resolved path, normally a absolute file path.
pub resolved_path: String,
/// whether this module should be external, if true, the module won't present in the final result
pub external: bool,
/// whether this module has side effects, affects tree shaking. By default, it's true, means all modules may has side effects.
/// use sideEffects field in package.json to mark it as side effects free
pub side_effects: bool,
/// the query parsed from specifier, for example, query should be `{ inline: "" }` if specifier is `./a.png?inline`
/// if you custom plugins, your plugin should be responsible for parsing query
/// if you just want a normal query parsing like the example above, [farmfe_toolkit::resolve::parse_query] should be helpful
pub query: Vec<(String, String)>,
/// the meta data passed between plugins and hooks
pub meta: HashMap<String, String>,
}
impl Default for PluginResolveHookResult {
fn default() -> Self {
Self {
side_effects: true,
resolved_path: "unknown".to_string(),
external: false,
query: vec![],
meta: Default::default(),
}
}
}