Comparing version 5.17.6 to 5.19.2
# Changelog | ||
## v5.19.2 | ||
- fix performance hit from avoiding HTML comments in the output | ||
## v5.19.1 | ||
- Better avoid outputting `</script>` and HTML comments. | ||
- Fix unused variables in class static blocks not being dropped correctly. | ||
- Fix sourcemap names of methods that are `async` or `static` | ||
## v5.19.0 | ||
- Allow `/*@__MANGLE_PROP__*/` annotation in `object.property`, in addition to property declarations. | ||
## v5.18.2 | ||
- Stop using recursion in hoisted defuns fix. | ||
## v5.18.1 | ||
- Fix major performance issue caused by hoisted defuns' scopes bugfix. | ||
## v5.18.0 | ||
- Add new `/*@__MANGLE_PROP__*/` annotation, to mark properties that should be mangled. | ||
## v5.17.7 | ||
- Update some dependencies | ||
- Add consistent sorting for `v` RegExp flag | ||
- Add `inert` DOM attribute to domprops | ||
## v5.17.6 | ||
@@ -4,0 +29,0 @@ - Fixes to mozilla AST input and output, for class properties, private properties and static blocks |
@@ -276,17 +276,20 @@ /*********************************************************************** | ||
} | ||
if ((node instanceof AST_Defun || node instanceof AST_DefClass) && node !== self) { | ||
if (node instanceof AST_DefClass && node !== self) { | ||
const def = node.name.definition(); | ||
descend(node, this); | ||
const keep_class = def.global && !drop_funcs || in_use_ids.has(def.id); | ||
if (!keep_class) { | ||
const kept = node.drop_side_effect_free(compressor); | ||
if (kept == null) { | ||
def.eliminated++; | ||
return in_list ? MAP.skip : make_node(AST_EmptyStatement, node); | ||
} | ||
return kept; | ||
} | ||
return node; | ||
} | ||
if (node instanceof AST_Defun && node !== self) { | ||
const def = node.name.definition(); | ||
const keep = def.global && !drop_funcs || in_use_ids.has(def.id); | ||
if (!keep) { | ||
// Class "extends" and static blocks may have side effects | ||
if (node instanceof AST_Class) { | ||
const kept = node.drop_side_effect_free(compressor); | ||
if (kept !== node) { | ||
def.eliminated++; | ||
if (kept) return kept; | ||
return in_list ? MAP.skip : make_node(AST_EmptyStatement, node); | ||
} else { | ||
return kept; | ||
} | ||
} | ||
def.eliminated++; | ||
@@ -293,0 +296,0 @@ return in_list ? MAP.skip : make_node(AST_EmptyStatement, node); |
@@ -96,6 +96,6 @@ /*********************************************************************** | ||
walk, | ||
walk_parent, | ||
walk_abort, | ||
walk_body, | ||
TreeWalker, | ||
_INLINE, | ||
@@ -517,2 +517,6 @@ _NOINLINE, | ||
const symbols_of_interest = new Set(); | ||
const defuns_of_interest = new Set(); | ||
const potential_conflicts = []; | ||
for (const defun of defuns) { | ||
@@ -547,31 +551,75 @@ const fname_def = defun.name.definition(); | ||
// Detect `call_defun(); var used_in_defun = X` | ||
// Because `used_in_defun` is not certainly X when it's defined after. | ||
let found_defun_ref = false; | ||
let found_def_after_defun = false; | ||
walk_parent(parent, (node, info) => { | ||
if (node === defun) return true; | ||
// for the slower checks below this loop | ||
potential_conflicts.push({ defun, def, fname_def }); | ||
symbols_of_interest.add(def.id); | ||
symbols_of_interest.add(fname_def.id); | ||
defuns_of_interest.add(defun); | ||
} | ||
} | ||
// Step 1: find `call_defun()` or other refs to the defun | ||
if ( | ||
!found_defun_ref | ||
&& node.thedef === fname_def | ||
&& node instanceof AST_Symbol | ||
) { | ||
found_defun_ref = true; | ||
// linearize all symbols, and locate defs that are read after the defun | ||
if (potential_conflicts.length) { | ||
// All "symbols of interest", that is, defuns or defs, that we found. | ||
// These are placed in order so we can check which is after which. | ||
const found_symbols = []; | ||
// Indices of `found_symbols` which are writes | ||
const found_symbol_writes = new Set(); | ||
// Defun ranges are recorded because we don't care if a function uses the def internally | ||
const defun_ranges = new Map(); | ||
let tw; | ||
parent.walk((tw = new TreeWalker((node, descend) => { | ||
if (node instanceof AST_Defun && defuns_of_interest.has(node)) { | ||
const start = found_symbols.length; | ||
descend(); | ||
const end = found_symbols.length; | ||
defun_ranges.set(node, { start, end }); | ||
return true; | ||
} | ||
// if we found a defun on the list, mark IN_DEFUN=id and descend | ||
if (node instanceof AST_Symbol && node.thedef) { | ||
const id = node.definition().id; | ||
if (symbols_of_interest.has(id)) { | ||
if (node instanceof AST_SymbolDeclaration || is_lhs(node, tw)) { | ||
found_symbol_writes.add(found_symbols.length); | ||
} | ||
found_symbols.push(id); | ||
} | ||
} | ||
}))); | ||
// Step 2: if Step 1 occurred, find a var the defun uses | ||
if ( | ||
found_defun_ref | ||
&& node.thedef === def | ||
&& (node instanceof AST_SymbolDeclaration | ||
|| is_lhs(node, info)) | ||
) { | ||
found_def_after_defun = true; | ||
return walk_abort; | ||
for (const { def, defun, fname_def } of potential_conflicts) { | ||
const defun_range = defun_ranges.get(defun); | ||
// find the index in `found_symbols`, with some special rules: | ||
const find = (sym_id, starting_at = 0, must_be_write = false) => { | ||
let index = starting_at; | ||
for (;;) { | ||
index = found_symbols.indexOf(sym_id, index); | ||
if (index === -1) { | ||
break; | ||
} else if (index >= defun_range.start && index < defun_range.end) { | ||
index = defun_range.end; | ||
continue; | ||
} else if (must_be_write && !found_symbol_writes.has(index)) { | ||
index++; | ||
continue; | ||
} else { | ||
break; | ||
} | ||
} | ||
}); | ||
if (found_def_after_defun) { | ||
return index; | ||
}; | ||
const read_defun_at = find(fname_def.id); | ||
const wrote_def_at = find(def.id, read_defun_at + 1, true); | ||
const wrote_def_after_reading_defun = read_defun_at != -1 && wrote_def_at != -1 && wrote_def_at > read_defun_at; | ||
if (wrote_def_after_reading_defun) { | ||
def.fixed = false; | ||
@@ -578,0 +626,0 @@ } |
@@ -20,2 +20,3 @@ "use strict"; | ||
reserve_quoted_keys, | ||
find_annotated_props, | ||
} from "./propmangle.js"; | ||
@@ -239,2 +240,6 @@ | ||
} | ||
var annotated_props; | ||
if (options.mangle && options.mangle.properties) { | ||
annotated_props = find_annotated_props(toplevel); | ||
} | ||
if (options.wrap) { | ||
@@ -273,3 +278,3 @@ toplevel = toplevel.wrap_commonjs(options.wrap); | ||
if (options.mangle && options.mangle.properties) { | ||
toplevel = mangle_properties(toplevel, options.mangle.properties); | ||
toplevel = mangle_properties(toplevel, options.mangle.properties, annotated_props); | ||
} | ||
@@ -276,0 +281,0 @@ |
@@ -73,2 +73,5 @@ /*********************************************************************** | ||
_KEY, | ||
_MANGLEPROP, | ||
walk, | ||
} from "./ast.js"; | ||
@@ -181,3 +184,36 @@ import { domprops } from "../tools/domprops.js"; | ||
function mangle_properties(ast, options) { | ||
function find_annotated_props(ast) { | ||
var annotated_props = new Set(); | ||
walk(ast, node => { | ||
if ( | ||
node instanceof AST_ClassPrivateProperty | ||
|| node instanceof AST_PrivateMethod | ||
|| node instanceof AST_PrivateGetter | ||
|| node instanceof AST_PrivateSetter | ||
|| node instanceof AST_DotHash | ||
) { | ||
// handled by mangle_private_properties | ||
} else if (node instanceof AST_ObjectKeyVal) { | ||
if (typeof node.key == "string" && has_annotation(node, _MANGLEPROP)) { | ||
annotated_props.add(node.key); | ||
} | ||
} else if (node instanceof AST_ObjectProperty) { | ||
// setter or getter, since KeyVal is handled above | ||
if (has_annotation(node, _MANGLEPROP)) { | ||
annotated_props.add(node.key.name); | ||
} | ||
} else if (node instanceof AST_Dot) { | ||
if (has_annotation(node, _MANGLEPROP)) { | ||
annotated_props.add(node.property); | ||
} | ||
} else if (node instanceof AST_Sub) { | ||
if (node.property instanceof AST_String && has_annotation(node, _MANGLEPROP)) { | ||
annotated_props.add(node.property.value); | ||
} | ||
} | ||
}); | ||
return annotated_props; | ||
} | ||
function mangle_properties(ast, options, annotated_props = find_annotated_props(ast)) { | ||
options = defaults(options, { | ||
@@ -193,2 +229,3 @@ builtins: false, | ||
undeclared: false, | ||
only_annotated: false, | ||
}, true); | ||
@@ -212,2 +249,3 @@ | ||
var only_annotated = options.only_annotated; | ||
var regex = options.regex && new RegExp(options.regex); | ||
@@ -328,3 +366,6 @@ | ||
function should_mangle(name) { | ||
if (regex && !regex.test(name)) return false; | ||
if (only_annotated && !annotated_props.has(name)) return false; | ||
if (regex && !regex.test(name)) { | ||
return annotated_props.has(name); | ||
} | ||
if (reserved.has(name)) return false; | ||
@@ -336,4 +377,5 @@ return cache.has(name) | ||
function add(name) { | ||
if (can_mangle(name)) | ||
if (can_mangle(name)) { | ||
names_to_mangle.add(name); | ||
} | ||
@@ -395,2 +437,3 @@ if (!should_mangle(name)) { | ||
mangle_private_properties, | ||
find_annotated_props, | ||
}; |
@@ -240,3 +240,3 @@ /*********************************************************************** | ||
const all_flags = "dgimsuy"; | ||
const all_flags = "dgimsuyv"; | ||
function sort_regexp_flags(flags) { | ||
@@ -243,0 +243,0 @@ const existing_flags = new Set(flags.split("")); |
@@ -7,3 +7,3 @@ { | ||
"license": "BSD-2-Clause", | ||
"version": "5.17.6", | ||
"version": "5.19.2", | ||
"engines": { | ||
@@ -48,4 +48,4 @@ "node": ">=10" | ||
"dependencies": { | ||
"@jridgewell/source-map": "^0.3.2", | ||
"acorn": "^8.5.0", | ||
"@jridgewell/source-map": "^0.3.3", | ||
"acorn": "^8.8.2", | ||
"commander": "^2.20.0", | ||
@@ -55,4 +55,4 @@ "source-map-support": "~0.5.20" | ||
"devDependencies": { | ||
"@ls-lint/ls-lint": "^1.10.0", | ||
"astring": "^1.7.5", | ||
"@ls-lint/ls-lint": "^1.11.2", | ||
"astring": "^1.8.5", | ||
"eslint": "^7.32.0", | ||
@@ -63,5 +63,4 @@ "eslump": "^3.0.0", | ||
"pre-commit": "^1.2.2", | ||
"rimraf": "^3.0.2", | ||
"rollup": "2.56.3", | ||
"semver": "^7.3.4", | ||
"rollup": "^2.56.3", | ||
"semver": "^7.5.1", | ||
"source-map": "~0.8.0-beta.0" | ||
@@ -76,3 +75,3 @@ }, | ||
"ls-lint": "ls-lint", | ||
"build": "rimraf dist/bundle* && rollup --config --silent", | ||
"build": "rollup --config --silent", | ||
"prepare": "npm run build", | ||
@@ -79,0 +78,0 @@ "postversion": "echo 'Remember to update the changelog!'" |
@@ -55,3 +55,5 @@ <h1><img src="https://terser.org/img/terser-banner-logo.png" alt="Terser" width="400"></h1> | ||
terser [input files] [options] | ||
``` | ||
terser [input files] [options] | ||
``` | ||
@@ -110,2 +112,3 @@ Terser can take multiple input files. It's recommended that you pass the | ||
`regex` Only mangle matched property names. | ||
`only_annotated` Only mangle properties defined with /*@__MANGLE_PROP__*/. | ||
`reserved` List of names that should not be mangled. | ||
@@ -678,2 +681,6 @@ -f, --format [options] Specify format options. | ||
<!-- API_REFERENCE:END --> | ||
<!-- OPTIONS:START --> | ||
## Parse options | ||
@@ -1067,4 +1074,10 @@ | ||
<!-- OPTIONS:END --> | ||
# Miscellaneous | ||
<!-- MISCELLANEOUS:START --> | ||
### Keeping copyright notices or other comments | ||
@@ -1196,2 +1209,3 @@ | ||
* `/*@__KEY__*/` - Marks a string literal as a property to also mangle it when mangling properties. | ||
* `/*@__MANGLE_PROP__*/` - Opts-in an object property (or class field) for mangling, when the property mangler is enabled. | ||
@@ -1334,15 +1348,23 @@ You can use either a `@` sign at the start, or a `#`. | ||
<!-- API_REFERENCE:END --> | ||
<!-- MISCELLANEOUS:END --> | ||
# Reporting issues | ||
In the terser CLI we use [source-map-support](https://npmjs.com/source-map-support) to produce good error stacks. In your own app, you're expected to enable source-map-support (read their docs) to have nice stack traces that will help you write good issues. | ||
<!-- REPORTING_ISSUES:START --> | ||
## A minimal, reproducible example | ||
You're expected to provide a [minimal reproducible example] of input code that will demonstrate your issue. | ||
To get to this example, you can remove bits of your code and stop if your issue ceases to reproduce. | ||
## Obtaining the source code given to Terser | ||
Because users often don't control the call to `await minify()` or its arguments, Terser provides a `TERSER_DEBUG_DIR` environment variable to make terser output some debug logs. If you're using a bundler or a project that includes a bundler and are not sure what went wrong with your code, pass that variable like so: | ||
Because users often don't control the call to `await minify()` or its arguments, Terser provides a `TERSER_DEBUG_DIR` environment variable to make terser output some debug logs. | ||
``` | ||
$ TERSER_DEBUG_DIR=/path/to/logs command-that-uses-terser | ||
$ ls /path/to/logs | ||
These logs will contain the input code and options of each `minify()` call. | ||
```bash | ||
TERSER_DEBUG_DIR=/tmp/terser-log-dir command-that-uses-terser | ||
ls /tmp/terser-log-dir | ||
terser-debug-123456.log | ||
@@ -1357,2 +1379,8 @@ ``` | ||
## Stack traces | ||
In the terser CLI we use [source-map-support](https://npmjs.com/source-map-support) to produce good error stacks. In your own app, you're expected to enable source-map-support (read their docs) to have nice stack traces that will help you write good issues. | ||
<!-- REPORTING_ISSUES:END --> | ||
# README.md Patrons: | ||
@@ -1359,0 +1387,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2101965
10
59653
1417
Updatedacorn@^8.8.2