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
//! prettify-js is a tokenizer-based JS prettyprinter that generates source maps.
//!
//! Example:
//! ```
//! let (pretty, _) = prettify_js::prettyprint("function x(a){return a;}");
//! assert_eq!(pretty, "function x(a) {\n  return a;\n}\n");
//! ```

mod prettyprint;
mod source_map_generator;

pub use prettyprint::*;
pub use source_map_generator::*;

/// Uses a heuristic to decide if the source file needs prettyprinting:
/// whether its average line length is > 100.
///
/// We also prettyprint if the source string starts with "//PRETTYPRINT".
/// This is useful for testing.
pub fn should_prettyprint(source_str: &str) -> bool {
    if source_str.starts_with("//PRETTYPRINT") {
        return true;
    }
    let mut lines = 0;
    let mut line_lengths = 0;
    for line in source_str.lines() {
        lines += 1;
        line_lengths += line.len();
    }
    if lines == 0 {
        return false;
    }
    line_lengths / lines > 100
}

/// Convenience function to create a sourcemap for the prettyprinted version of the file
/// (if it needs prettyprinting), generate a URL for it and append that URL to the file text
/// so it gets used.
///
/// The `generate_file` closure takes a file name and file text and returns a URL which can
/// be used to load that file. This URL is injected into `source_str`.
///
/// Example:
/// ```
/// let mut generated = "//PRETTYPRINT\nfunction x(a){return a;}".to_string();
/// prettify_js::maybe_prettyprint("demo.js", &mut generated,
///   |name, text| {
///     assert_eq!(name, "demo.js.sourcemap");
///     let url = "https://example.com/demo.js.sourcemap".to_string();
///     println!("Serve {} with contents {}", &url, text);
///     url
///   });
/// assert_eq!(generated, "//PRETTYPRINT\nfunction x(a){return a;}\n//# sourceMappingURL=https://example.com/demo.js.sourcemap");
/// ```
pub fn maybe_prettyprint<G>(script_name: &str, source_str: &mut String, mut generate_file: G)
where
    G: FnMut(String, String) -> String,
{
    if !should_prettyprint(source_str) {
        return;
    }
    let (pretty_str, mappings) = prettyprint(&source_str);
    let source_map_name = format!("{}.sourcemap", script_name);
    let pretty_name = format!("{}.pretty", script_name);
    // The source map maps *from* prettyprinted source *to* the obfuscated/minified source
    let source_map = generate_source_map(pretty_name, pretty_str, mappings);
    let url = generate_file(source_map_name, source_map);
    source_str.push_str("\n//# sourceMappingURL=");
    source_str.push_str(&url);
}