prettify_js/
lib.rs

1//! prettify-js is a tokenizer-based JS prettyprinter that generates source maps.
2//!
3//! Example:
4//! ```
5//! let (pretty, _) = prettify_js::prettyprint("function x(a){return a;}");
6//! assert_eq!(pretty, "function x(a) {\n  return a;\n}\n");
7//! ```
8
9mod prettyprint;
10mod source_map_generator;
11
12pub use prettyprint::*;
13pub use source_map_generator::*;
14
15/// Uses a heuristic to decide if the source file needs prettyprinting:
16/// whether its average line length is > 100.
17///
18/// We also prettyprint if the source string starts with "//PRETTYPRINT".
19/// This is useful for testing.
20pub fn should_prettyprint(source_str: &str) -> bool {
21    if source_str.starts_with("//PRETTYPRINT") {
22        return true;
23    }
24    let mut lines = 0;
25    let mut line_lengths = 0;
26    for line in source_str.lines() {
27        lines += 1;
28        line_lengths += line.len();
29    }
30    if lines == 0 {
31        return false;
32    }
33    line_lengths / lines > 100
34}
35
36/// Convenience function to create a sourcemap for the prettyprinted version of the file
37/// (if it needs prettyprinting), generate a URL for it and append that URL to the file text
38/// so it gets used.
39///
40/// The `generate_file` closure takes a file name and file text and returns a URL which can
41/// be used to load that file. This URL is injected into `source_str`.
42///
43/// Example:
44/// ```
45/// let mut generated = "//PRETTYPRINT\nfunction x(a){return a;}".to_string();
46/// prettify_js::maybe_prettyprint("demo.js", &mut generated,
47///   |name, text| {
48///     assert_eq!(name, "demo.js.sourcemap");
49///     let url = "https://example.com/demo.js.sourcemap".to_string();
50///     println!("Serve {} with contents {}", &url, text);
51///     url
52///   });
53/// assert_eq!(generated, "//PRETTYPRINT\nfunction x(a){return a;}\n//# sourceMappingURL=https://example.com/demo.js.sourcemap");
54/// ```
55pub fn maybe_prettyprint<G>(script_name: &str, source_str: &mut String, mut generate_file: G)
56where
57    G: FnMut(String, String) -> String,
58{
59    if !should_prettyprint(source_str) {
60        return;
61    }
62    let (pretty_str, mappings) = prettyprint(&source_str);
63    let source_map_name = format!("{}.sourcemap", script_name);
64    let pretty_name = format!("{}.pretty", script_name);
65    // The source map maps *from* prettyprinted source *to* the obfuscated/minified source
66    let source_map = generate_source_map(pretty_name, pretty_str, mappings);
67    let url = generate_file(source_map_name, source_map);
68    source_str.push_str("\n//# sourceMappingURL=");
69    source_str.push_str(&url);
70}