webpack_stats/v5/
module.rs

1/*
2 * Copyright [2022] [Kevin Velasco]
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use crate::common::chunk::ChunkId;
18use crate::common::{DurationMillis, SizeBytes};
19use std::collections::{HashMap, HashSet};
20use std::iter::once;
21// use crate::v5::asset::Asset;
22use crate::v5::reason::Reasons;
23use empty_type::{Empty, EmptyType};
24use serde::{Deserialize, Deserializer};
25
26use crate::common::import::{ImportType, SourceText};
27use crate::common::module::{ModuleId, ModuleIdentifier, ModuleName};
28use crate::import::ResolvedModule;
29use crate::module::{IncludedModuleNames, ModuleChunks};
30use meshed::prelude::*;
31
32#[derive(Debug, Default)]
33pub struct Modules<'a> {
34    pub modules: Vec<Module<'a>>,
35}
36
37impl<'a> crate::common::module::Modules<Module<'a>> for Modules<'a> {}
38
39impl<'de: 'a, 'a> Deserialize<'de> for Modules<'a> {
40    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
41    where
42        D: Deserializer<'de>,
43    {
44        let empty_vec: Vec<Empty<Module<'a>>> = Deserialize::deserialize(deserializer)?;
45
46        let materialized_vec = empty_vec.into_iter().map(|i| i.resolve()).collect();
47        Ok(Self {
48            modules: materialized_vec,
49        })
50    }
51}
52
53impl<'a> Query<ModuleIdentifier, Module<'a>> for Modules<'a> {
54    fn query(&self, _identifier: &ModuleIdentifier) -> Option<&Module<'a>> {
55        panic!("Query should be called on the module's index not on the modules iterator itself.")
56    }
57
58    fn all(&self) -> Vec<&Module<'a>> {
59        self.modules
60            .iter()
61            .flat_map(|module| module.modules.all().into_iter().chain(once(module)))
62            .collect()
63    }
64
65    fn create_index(&self) -> HashMap<ModuleIdentifier, Link<ModuleIdentifier, Module<'a>>> {
66        let mut map: HashMap<_, _> = Default::default();
67
68        for module in self.modules.iter() {
69            for child in module.modules.modules.iter() {
70                map.insert(child.get_id(), Link::Link(module.get_id()));
71            }
72            map.insert(module.get_id(), Link::Value(module));
73        }
74        map
75    }
76}
77
78impl<'a> ExtractData<IncludedModuleNames> for Module<'a> {
79    fn extract_data(&self) -> IncludedModuleNames {
80        let mut included_names: HashSet<_> = Default::default();
81        included_names.insert(self.name.0.to_string());
82
83        for child in self.modules.all() {
84            included_names.insert(child.name.0.to_string());
85            let data: IncludedModuleNames = child.extract_data();
86            included_names.extend(data.0.into_iter());
87        }
88
89        IncludedModuleNames(included_names)
90    }
91}
92
93impl<'a> ExtractData<ModuleChunks> for Module<'a> {
94    fn extract_data(&self) -> ModuleChunks {
95        HashSet::from_iter(self.chunks.iter().cloned())
96    }
97}
98
99impl<'a> Label for Module<'a> {
100    type Label = ModuleName;
101
102    fn label(&self) -> Self::Label {
103        self.name.clone()
104    }
105}
106
107impl<'a> crate::common::module::Module for Module<'a> {}
108
109impl<'a> Identifiable<ModuleIdentifier> for Module<'a> {
110    fn get_id(&self) -> ModuleIdentifier {
111        self.identifier.clone()
112    }
113}
114
115impl<'a> Edges<ModuleIdentifier, (ImportType, ResolvedModule)> for Module<'a> {
116    fn next_edge(
117        &self,
118        previous_edge_index: Option<usize>,
119    ) -> Option<Edge<ModuleIdentifier, (ImportType, ResolvedModule)>> {
120        let next_index = previous_edge_index.map(|e| e + 1).unwrap_or_default();
121        let reason = self.reasons.get(next_index)?;
122        Some(Edge::new(
123            self.get_id(),
124            reason.module_identifier.clone(),
125            next_index,
126            (
127                reason.r#type,
128                ResolvedModule(reason.resolved_module.clone()),
129            ),
130        ))
131    }
132}
133
134#[derive(Deserialize, Debug, EmptyType)]
135#[serde(rename_all = "camelCase")]
136#[empty(bounds = "'a", deserialize)]
137pub struct Module<'a> {
138    // #[serde(borrow)]
139    pub assets: Vec<serde_json::Value>,
140    /// Indicates that the module went through loaders,
141    /// Parsing, and Code Generation
142    pub built: bool,
143    #[empty(fail_safe)]
144    pub cacheable: bool,
145    pub chunks: Vec<ChunkId>,
146
147    // Webpack naming is bad.
148    #[serde(rename = "errors")]
149    pub error_count: u32,
150    #[serde(rename = "warnings")]
151    pub warning_count: u32,
152
153    pub failed: bool,
154    /// Possibly a relic of the past? Also undocumented by webpack. ModuleIdentifier / identifier
155    /// is a better unique name. Use that if possible.
156    #[empty(fail_safe)]
157    pub id: Option<ModuleId>,
158    pub identifier: ModuleIdentifier,
159    pub name: ModuleName,
160    pub optional: bool,
161    #[serde(default)]
162    pub prefetched: bool,
163    /// Every module also contains a list of reasons objects describing why
164    /// that module was included in the dependency graph. Each "reason" is similar to the origins
165    #[serde(borrow)]
166    pub reasons: Reasons<'a>,
167    pub size: SizeBytes,
168    pub source: Option<SourceText<'a>>,
169    #[empty(default)]
170    pub profile: Profile,
171    #[empty(default)]
172    pub modules: Modules<'a>,
173}
174
175#[derive(Deserialize, Debug, Default)]
176pub struct Profile {
177    pub building: DurationMillis,
178    pub dependencies: DurationMillis,
179    pub factory: DurationMillis,
180}