terser 5.13.1 → 5.14.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## v5.14.2
4
+
5
+ - Security fix for RegExps that should not be evaluated (regexp DDOS)
6
+ - Source maps improvements (#1211)
7
+ - Performance improvements in long property access evaluation (#1213)
8
+
9
+ ## v5.14.1
10
+ - keep_numbers option added to TypeScript defs (#1208)
11
+ - Fixed parsing of nested template strings (#1204)
12
+
13
+ ## v5.14.0
14
+ - Switched to @jridgewell/source-map for sourcemap generation (#1190, #1181)
15
+ - Fixed source maps with non-terminated segments (#1106)
16
+ - Enabled typescript types to be imported from the package (#1194)
17
+ - Extra DOM props have been added (#1191)
18
+ - Delete the AST while generating code, as a means to save RAM
19
+
3
20
  ## v5.13.1
4
21
  - Removed self-assignments (`varname=varname`) (closes #1081)
5
22
  - Separated inlining code (for inlining things into references, or removing IIFEs)
@@ -195,6 +212,13 @@ Hotfix release, fixes package.json "engines" syntax
195
212
  - Module is now distributed as a dual package - You can `import` and `require()` too.
196
213
  - Inline improvements were made
197
214
 
215
+
216
+ -----
217
+
218
+ ## v4.8.1 (backport)
219
+
220
+ - Security fix for RegExps that should not be evaluated (regexp DDOS)
221
+
198
222
  ## v4.8.0
199
223
 
200
224
  - Support for numeric separators (`million = 1_000_000`) was added.
@@ -1,12 +1,8 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('source-map')) :
3
- typeof define === 'function' && define.amd ? define(['exports', 'source-map'], factory) :
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@jridgewell/source-map')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', '@jridgewell/source-map'], factory) :
4
4
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Terser = {}, global.sourceMap));
5
- }(this, (function (exports, MOZ_SourceMap) { 'use strict';
6
-
7
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
-
9
- var MOZ_SourceMap__default = /*#__PURE__*/_interopDefaultLegacy(MOZ_SourceMap);
5
+ }(this, (function (exports, sourceMap) { 'use strict';
10
6
 
11
7
  /***********************************************************************
12
8
 
@@ -257,7 +253,15 @@ function regexp_source_fix(source) {
257
253
  return (escaped ? "" : "\\") + lineTerminatorEscape[match];
258
254
  });
259
255
  }
260
- const all_flags = "gimuy";
256
+
257
+ // Subset of regexps that is not going to cause regexp based DDOS
258
+ // https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
259
+ const re_safe_regexp = /^[\\/|\0\s\w^$.[\]()]*$/;
260
+
261
+ /** Check if the regexp is safe for Terser to create without risking a RegExp DOS */
262
+ const regexp_is_safe = (source) => re_safe_regexp.test(source);
263
+
264
+ const all_flags = "dgimsuy";
261
265
  function sort_regexp_flags(flags) {
262
266
  const existing_flags = new Set(flags.split(""));
263
267
  let out = "";
@@ -327,7 +331,7 @@ function set_annotation(node, annotation) {
327
331
  ***********************************************************************/
328
332
 
329
333
  var LATEST_RAW = ""; // Only used for numbers and template strings
330
- var LATEST_TEMPLATE_END = true;
334
+ var TEMPLATE_RAWS = new Map(); // Raw template strings
331
335
 
332
336
  var KEYWORDS = "break case catch class const continue debugger default delete do else export extends finally for function if in instanceof let new return switch throw try typeof var void while with";
333
337
  var KEYWORDS_ATOM = "false null true";
@@ -858,8 +862,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
858
862
  next(true, true);
859
863
  S.brace_counter++;
860
864
  tok = token(begin ? "template_head" : "template_substitution", content);
861
- LATEST_RAW = raw;
862
- LATEST_TEMPLATE_END = false;
865
+ TEMPLATE_RAWS.set(tok, raw);
866
+ tok.template_end = false;
863
867
  return tok;
864
868
  }
865
869
 
@@ -875,8 +879,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
875
879
  }
876
880
  S.template_braces.pop();
877
881
  tok = token(begin ? "template_head" : "template_substitution", content);
878
- LATEST_RAW = raw;
879
- LATEST_TEMPLATE_END = true;
882
+ TEMPLATE_RAWS.set(tok, raw);
883
+ tok.template_end = true;
880
884
  return tok;
881
885
  });
882
886
 
@@ -2532,19 +2536,19 @@ function parse($TEXT, options) {
2532
2536
 
2533
2537
  segments.push(new AST_TemplateSegment({
2534
2538
  start: S.token,
2535
- raw: LATEST_RAW,
2539
+ raw: TEMPLATE_RAWS.get(S.token),
2536
2540
  value: S.token.value,
2537
2541
  end: S.token
2538
2542
  }));
2539
2543
 
2540
- while (!LATEST_TEMPLATE_END) {
2544
+ while (!S.token.template_end) {
2541
2545
  next();
2542
2546
  handle_regexp();
2543
2547
  segments.push(expression(true));
2544
2548
 
2545
2549
  segments.push(new AST_TemplateSegment({
2546
2550
  start: S.token,
2547
- raw: LATEST_RAW,
2551
+ raw: TEMPLATE_RAWS.get(S.token),
2548
2552
  value: S.token.value,
2549
2553
  end: S.token
2550
2554
  }));
@@ -3507,6 +3511,7 @@ function parse($TEXT, options) {
3507
3511
  } else {
3508
3512
  toplevel = new AST_Toplevel({ start: start, body: body, end: end });
3509
3513
  }
3514
+ TEMPLATE_RAWS = new Map();
3510
3515
  return toplevel;
3511
3516
  })();
3512
3517
 
@@ -3600,6 +3605,7 @@ const set_tok_flag = (tok, flag, truth) => {
3600
3605
  const TOK_FLAG_NLB = 0b0001;
3601
3606
  const TOK_FLAG_QUOTE_SINGLE = 0b0010;
3602
3607
  const TOK_FLAG_QUOTE_EXISTS = 0b0100;
3608
+ const TOK_FLAG_TEMPLATE_END = 0b1000;
3603
3609
 
3604
3610
  class AST_Token {
3605
3611
  constructor(type, value, line, col, pos, nlb, comments_before, comments_after, file) {
@@ -3635,6 +3641,14 @@ class AST_Token {
3635
3641
  set_tok_flag(this, TOK_FLAG_QUOTE_SINGLE, quote_type === "'");
3636
3642
  set_tok_flag(this, TOK_FLAG_QUOTE_EXISTS, !!quote_type);
3637
3643
  }
3644
+
3645
+ get template_end() {
3646
+ return has_tok_flag(this, TOK_FLAG_TEMPLATE_END);
3647
+ }
3648
+
3649
+ set template_end(new_template_end) {
3650
+ set_tok_flag(this, TOK_FLAG_TEMPLATE_END, new_template_end);
3651
+ }
3638
3652
  }
3639
3653
 
3640
3654
  var AST_Node = DEFNODE("Node", "start end", function AST_Node(props) {
@@ -4059,11 +4073,10 @@ var AST_With = DEFNODE("With", "expression", function AST_With(props) {
4059
4073
 
4060
4074
  var AST_Scope = DEFNODE(
4061
4075
  "Scope",
4062
- "variables functions uses_with uses_eval parent_scope enclosed cname",
4076
+ "variables uses_with uses_eval parent_scope enclosed cname",
4063
4077
  function AST_Scope(props) {
4064
4078
  if (props) {
4065
4079
  this.variables = props.variables;
4066
- this.functions = props.functions;
4067
4080
  this.uses_with = props.uses_with;
4068
4081
  this.uses_eval = props.uses_eval;
4069
4082
  this.parent_scope = props.parent_scope;
@@ -4119,7 +4132,6 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", function AST_Toplevel(props) {
4119
4132
  if (props) {
4120
4133
  this.globals = props.globals;
4121
4134
  this.variables = props.variables;
4122
- this.functions = props.functions;
4123
4135
  this.uses_with = props.uses_with;
4124
4136
  this.uses_eval = props.uses_eval;
4125
4137
  this.parent_scope = props.parent_scope;
@@ -4201,7 +4213,6 @@ var AST_Lambda = DEFNODE(
4201
4213
  this.is_generator = props.is_generator;
4202
4214
  this.async = props.async;
4203
4215
  this.variables = props.variables;
4204
- this.functions = props.functions;
4205
4216
  this.uses_with = props.uses_with;
4206
4217
  this.uses_eval = props.uses_eval;
4207
4218
  this.parent_scope = props.parent_scope;
@@ -4281,7 +4292,6 @@ var AST_Accessor = DEFNODE("Accessor", null, function AST_Accessor(props) {
4281
4292
  this.is_generator = props.is_generator;
4282
4293
  this.async = props.async;
4283
4294
  this.variables = props.variables;
4284
- this.functions = props.functions;
4285
4295
  this.uses_with = props.uses_with;
4286
4296
  this.uses_eval = props.uses_eval;
4287
4297
  this.parent_scope = props.parent_scope;
@@ -4306,7 +4316,6 @@ var AST_Function = DEFNODE("Function", null, function AST_Function(props) {
4306
4316
  this.is_generator = props.is_generator;
4307
4317
  this.async = props.async;
4308
4318
  this.variables = props.variables;
4309
- this.functions = props.functions;
4310
4319
  this.uses_with = props.uses_with;
4311
4320
  this.uses_eval = props.uses_eval;
4312
4321
  this.parent_scope = props.parent_scope;
@@ -4331,7 +4340,6 @@ var AST_Arrow = DEFNODE("Arrow", null, function AST_Arrow(props) {
4331
4340
  this.is_generator = props.is_generator;
4332
4341
  this.async = props.async;
4333
4342
  this.variables = props.variables;
4334
- this.functions = props.functions;
4335
4343
  this.uses_with = props.uses_with;
4336
4344
  this.uses_eval = props.uses_eval;
4337
4345
  this.parent_scope = props.parent_scope;
@@ -4356,7 +4364,6 @@ var AST_Defun = DEFNODE("Defun", null, function AST_Defun(props) {
4356
4364
  this.is_generator = props.is_generator;
4357
4365
  this.async = props.async;
4358
4366
  this.variables = props.variables;
4359
- this.functions = props.functions;
4360
4367
  this.uses_with = props.uses_with;
4361
4368
  this.uses_eval = props.uses_eval;
4362
4369
  this.parent_scope = props.parent_scope;
@@ -5668,7 +5675,6 @@ var AST_Class = DEFNODE("Class", "name extends properties", function AST_Class(p
5668
5675
  this.extends = props.extends;
5669
5676
  this.properties = props.properties;
5670
5677
  this.variables = props.variables;
5671
- this.functions = props.functions;
5672
5678
  this.uses_with = props.uses_with;
5673
5679
  this.uses_eval = props.uses_eval;
5674
5680
  this.parent_scope = props.parent_scope;
@@ -5762,7 +5768,6 @@ var AST_DefClass = DEFNODE("DefClass", null, function AST_DefClass(props) {
5762
5768
  this.extends = props.extends;
5763
5769
  this.properties = props.properties;
5764
5770
  this.variables = props.variables;
5765
- this.functions = props.functions;
5766
5771
  this.uses_with = props.uses_with;
5767
5772
  this.uses_eval = props.uses_eval;
5768
5773
  this.parent_scope = props.parent_scope;
@@ -5785,7 +5790,6 @@ var AST_ClassExpression = DEFNODE("ClassExpression", null, function AST_ClassExp
5785
5790
  this.extends = props.extends;
5786
5791
  this.properties = props.properties;
5787
5792
  this.variables = props.variables;
5788
- this.functions = props.functions;
5789
5793
  this.uses_with = props.uses_with;
5790
5794
  this.uses_eval = props.uses_eval;
5791
5795
  this.parent_scope = props.parent_scope;
@@ -8644,6 +8648,8 @@ function OutputStream(options) {
8644
8648
  width : 80,
8645
8649
  wrap_iife : false,
8646
8650
  wrap_func_args : true,
8651
+
8652
+ _destroy_ast : false
8647
8653
  }, true);
8648
8654
 
8649
8655
  if (options.shorthand === undefined)
@@ -9193,6 +9199,18 @@ function OutputStream(options) {
9193
9199
  if (OUTPUT.length() > insert) newline_insert = insert;
9194
9200
  }
9195
9201
 
9202
+ /**
9203
+ * When output.option("_destroy_ast") is enabled, destroy the function.
9204
+ * Call this after printing it.
9205
+ */
9206
+ const gc_scope =
9207
+ options["_destroy_ast"]
9208
+ ? function gc_scope(scope) {
9209
+ scope.body.length = 0;
9210
+ scope.argnames.length = 0;
9211
+ }
9212
+ : noop;
9213
+
9196
9214
  var stack = [];
9197
9215
  return {
9198
9216
  get : get,
@@ -9239,6 +9257,7 @@ function OutputStream(options) {
9239
9257
  with_square : with_square,
9240
9258
  add_mapping : add_mapping,
9241
9259
  option : function(opt) { return options[opt]; },
9260
+ gc_scope,
9242
9261
  printed_comments: printed_comments,
9243
9262
  prepend_comments: readonly ? noop : prepend_comments,
9244
9263
  append_comments : readonly || comment_filter === return_false ? noop : append_comments,
@@ -9629,12 +9648,14 @@ function OutputStream(options) {
9629
9648
  output.with_indent(output.next_indent(), function() {
9630
9649
  output.append_comments(self, true);
9631
9650
  });
9651
+ output.add_mapping(self.end);
9632
9652
  output.print("}");
9633
9653
  }
9634
9654
  function print_braced(self, output, allow_directives) {
9635
9655
  if (self.body.length > 0) {
9636
9656
  output.with_block(function() {
9637
9657
  display_body(self.body, false, output, allow_directives);
9658
+ output.add_mapping(self.end);
9638
9659
  });
9639
9660
  } else print_braced_empty(self, output);
9640
9661
  }
@@ -9755,6 +9776,7 @@ function OutputStream(options) {
9755
9776
  });
9756
9777
  DEFPRINT(AST_Lambda, function(self, output) {
9757
9778
  self._do_print(output);
9779
+ output.gc_scope(self);
9758
9780
  });
9759
9781
 
9760
9782
  DEFPRINT(AST_PrefixedTemplateString, function(self, output) {
@@ -9834,6 +9856,7 @@ function OutputStream(options) {
9834
9856
  print_braced(self, output);
9835
9857
  }
9836
9858
  if (needs_parens) { output.print(")"); }
9859
+ output.gc_scope(self);
9837
9860
  });
9838
9861
 
9839
9862
  /* -----[ exits ]----- */
@@ -13838,7 +13861,7 @@ def_eval(AST_BigInt, return_this);
13838
13861
 
13839
13862
  def_eval(AST_RegExp, function (compressor) {
13840
13863
  let evaluated = compressor.evaluated_regexps.get(this.value);
13841
- if (evaluated === undefined) {
13864
+ if (evaluated === undefined && regexp_is_safe(this.value.source)) {
13842
13865
  try {
13843
13866
  const { source, flags } = this.value;
13844
13867
  evaluated = new RegExp(source, flags);
@@ -14051,7 +14074,7 @@ const regexp_flags = new Set([
14051
14074
  ]);
14052
14075
 
14053
14076
  def_eval(AST_PropAccess, function (compressor, depth) {
14054
- const obj = this.expression._eval(compressor, depth);
14077
+ let obj = this.expression._eval(compressor, depth + 1);
14055
14078
  if (obj === nullish || (this.optional && obj == null)) return nullish;
14056
14079
  if (compressor.option("unsafe")) {
14057
14080
  var key = this.property;
@@ -14061,7 +14084,6 @@ def_eval(AST_PropAccess, function (compressor, depth) {
14061
14084
  return this;
14062
14085
  }
14063
14086
  var exp = this.expression;
14064
- var val;
14065
14087
  if (is_undeclared_ref(exp)) {
14066
14088
 
14067
14089
  var aa;
@@ -14078,29 +14100,29 @@ def_eval(AST_PropAccess, function (compressor, depth) {
14078
14100
  }
14079
14101
  if (!is_pure_native_value(exp.name, key))
14080
14102
  return this;
14081
- val = global_objs[exp.name];
14103
+ obj = global_objs[exp.name];
14082
14104
  } else {
14083
- val = exp._eval(compressor, depth + 1);
14084
- if (val instanceof RegExp) {
14105
+ if (obj instanceof RegExp) {
14085
14106
  if (key == "source") {
14086
- return regexp_source_fix(val.source);
14107
+ return regexp_source_fix(obj.source);
14087
14108
  } else if (key == "flags" || regexp_flags.has(key)) {
14088
- return val[key];
14109
+ return obj[key];
14089
14110
  }
14090
14111
  }
14091
- if (!val || val === exp || !HOP(val, key))
14112
+ if (!obj || obj === exp || !HOP(obj, key))
14092
14113
  return this;
14093
- if (typeof val == "function")
14114
+
14115
+ if (typeof obj == "function")
14094
14116
  switch (key) {
14095
14117
  case "name":
14096
- return val.node.name ? val.node.name.name : "";
14118
+ return obj.node.name ? obj.node.name.name : "";
14097
14119
  case "length":
14098
- return val.node.length_property();
14120
+ return obj.node.length_property();
14099
14121
  default:
14100
14122
  return this;
14101
14123
  }
14102
14124
  }
14103
- return val[key];
14125
+ return obj[key];
14104
14126
  }
14105
14127
  return this;
14106
14128
  });
@@ -18979,6 +19001,7 @@ def_optimize(AST_Call, function(self, compressor) {
18979
19001
  params.push(value);
18980
19002
  return arg !== value;
18981
19003
  })
19004
+ && regexp_is_safe(params[0])
18982
19005
  ) {
18983
19006
  let [ source, flags ] = params;
18984
19007
  source = regexp_source_fix(new RegExp(source).source);
@@ -20638,6 +20661,12 @@ def_optimize(AST_Dot, function(self, compressor) {
20638
20661
  const sub = self.flatten_object(self.property, compressor);
20639
20662
  if (sub) return sub.optimize(compressor);
20640
20663
  }
20664
+
20665
+ if (self.expression instanceof AST_PropAccess
20666
+ && parent instanceof AST_PropAccess) {
20667
+ return self;
20668
+ }
20669
+
20641
20670
  let ev = self.evaluate(compressor);
20642
20671
  if (ev !== self) {
20643
20672
  ev = make_node_from_constant(ev, self).optimize(compressor);
@@ -21021,40 +21050,56 @@ def_optimize(AST_Destructuring, function(self, compressor) {
21021
21050
 
21022
21051
  ***********************************************************************/
21023
21052
 
21024
- // a small wrapper around fitzgen's source-map library
21053
+ // a small wrapper around source-map and @jridgewell/source-map
21025
21054
  async function SourceMap(options) {
21026
21055
  options = defaults(options, {
21027
21056
  file : null,
21028
21057
  root : null,
21029
21058
  orig : null,
21030
-
21031
- orig_line_diff : 0,
21032
- dest_line_diff : 0,
21059
+ files: {},
21033
21060
  });
21034
21061
 
21035
21062
  var orig_map;
21036
- var generator = new MOZ_SourceMap__default['default'].SourceMapGenerator({
21063
+ var generator = new sourceMap.SourceMapGenerator({
21037
21064
  file : options.file,
21038
21065
  sourceRoot : options.root
21039
21066
  });
21040
21067
 
21068
+ let sourcesContent = {__proto__: null};
21069
+ let files = options.files;
21070
+ for (var name in files) if (HOP(files, name)) {
21071
+ sourcesContent[name] = files[name];
21072
+ }
21041
21073
  if (options.orig) {
21042
- orig_map = await new MOZ_SourceMap__default['default'].SourceMapConsumer(options.orig);
21043
- orig_map.sources.forEach(function(source) {
21044
- var sourceContent = orig_map.sourceContentFor(source, true);
21045
- if (sourceContent) {
21046
- generator.setSourceContent(source, sourceContent);
21047
- }
21048
- });
21074
+ // We support both @jridgewell/source-map (which has a sync
21075
+ // SourceMapConsumer) and source-map (which has an async
21076
+ // SourceMapConsumer).
21077
+ orig_map = await new sourceMap.SourceMapConsumer(options.orig);
21078
+ if (orig_map.sourcesContent) {
21079
+ orig_map.sources.forEach(function(source, i) {
21080
+ var content = orig_map.sourcesContent[i];
21081
+ if (content) {
21082
+ sourcesContent[source] = content;
21083
+ }
21084
+ });
21085
+ }
21049
21086
  }
21050
21087
 
21051
21088
  function add(source, gen_line, gen_col, orig_line, orig_col, name) {
21089
+ let generatedPos = { line: gen_line, column: gen_col };
21090
+
21052
21091
  if (orig_map) {
21053
21092
  var info = orig_map.originalPositionFor({
21054
21093
  line: orig_line,
21055
21094
  column: orig_col
21056
21095
  });
21057
21096
  if (info.source === null) {
21097
+ generator.addMapping({
21098
+ generated: generatedPos,
21099
+ original: null,
21100
+ source: null,
21101
+ name: null
21102
+ });
21058
21103
  return;
21059
21104
  }
21060
21105
  source = info.source;
@@ -21063,22 +21108,42 @@ async function SourceMap(options) {
21063
21108
  name = info.name || name;
21064
21109
  }
21065
21110
  generator.addMapping({
21066
- generated : { line: gen_line + options.dest_line_diff, column: gen_col },
21067
- original : { line: orig_line + options.orig_line_diff, column: orig_col },
21111
+ generated : generatedPos,
21112
+ original : { line: orig_line, column: orig_col },
21068
21113
  source : source,
21069
21114
  name : name
21070
21115
  });
21116
+ generator.setSourceContent(source, sourcesContent[source]);
21117
+ }
21118
+
21119
+ function clean(map) {
21120
+ const allNull = map.sourcesContent && map.sourcesContent.every(c => c == null);
21121
+ if (allNull) delete map.sourcesContent;
21122
+ if (map.file === undefined) delete map.file;
21123
+ if (map.sourceRoot === undefined) delete map.sourceRoot;
21124
+ return map;
21125
+ }
21126
+
21127
+ function getDecoded() {
21128
+ if (!generator.toDecodedMap) return null;
21129
+ return clean(generator.toDecodedMap());
21130
+ }
21131
+
21132
+ function getEncoded() {
21133
+ return clean(generator.toJSON());
21134
+ }
21135
+
21136
+ function destroy() {
21137
+ // @jridgewell/source-map's SourceMapConsumer does not need to be
21138
+ // manually freed.
21139
+ if (orig_map && orig_map.destroy) orig_map.destroy();
21071
21140
  }
21072
21141
 
21073
21142
  return {
21074
- add : add,
21075
- get : function() { return generator; },
21076
- toString : function() { return generator.toString(); },
21077
- destroy : function () {
21078
- if (orig_map && orig_map.destroy) {
21079
- orig_map.destroy();
21080
- }
21081
- }
21143
+ add,
21144
+ getDecoded,
21145
+ getEncoded,
21146
+ destroy,
21082
21147
  };
21083
21148
  }
21084
21149
 
@@ -21404,6 +21469,7 @@ var domprops = [
21404
21469
  "COMMENT_NODE",
21405
21470
  "COMPARE_REF_TO_TEXTURE",
21406
21471
  "COMPILE_STATUS",
21472
+ "COMPLETION_STATUS_KHR",
21407
21473
  "COMPRESSED_RGBA_S3TC_DXT1_EXT",
21408
21474
  "COMPRESSED_RGBA_S3TC_DXT3_EXT",
21409
21475
  "COMPRESSED_RGBA_S3TC_DXT5_EXT",
@@ -29379,6 +29445,8 @@ async function minify(files, options, _fs_module) {
29379
29445
  url: null,
29380
29446
  }, true);
29381
29447
  }
29448
+
29449
+ // -- Parse phase --
29382
29450
  if (timings) timings.parse = Date.now();
29383
29451
  var toplevel;
29384
29452
  if (files instanceof AST_Toplevel) {
@@ -29422,12 +29490,16 @@ async function minify(files, options, _fs_module) {
29422
29490
  toplevel = toplevel.wrap_enclose(options.enclose);
29423
29491
  }
29424
29492
  if (timings) timings.rename = Date.now();
29493
+
29494
+ // -- Compress phase --
29425
29495
  if (timings) timings.compress = Date.now();
29426
29496
  if (options.compress) {
29427
29497
  toplevel = new Compressor(options.compress, {
29428
29498
  mangle_options: options.mangle
29429
29499
  }).compress(toplevel);
29430
29500
  }
29501
+
29502
+ // -- Mangle phase --
29431
29503
  if (timings) timings.scope = Date.now();
29432
29504
  if (options.mangle) toplevel.figure_out_scope(options.mangle);
29433
29505
  if (timings) timings.mangle = Date.now();
@@ -29440,6 +29512,8 @@ async function minify(files, options, _fs_module) {
29440
29512
  if (options.mangle && options.mangle.properties) {
29441
29513
  toplevel = mangle_properties(toplevel, options.mangle.properties);
29442
29514
  }
29515
+
29516
+ // Format phase
29443
29517
  if (timings) timings.format = Date.now();
29444
29518
  var result = {};
29445
29519
  if (options.format.ast) {
@@ -29449,19 +29523,34 @@ async function minify(files, options, _fs_module) {
29449
29523
  result.ast = toplevel.to_mozilla_ast();
29450
29524
  }
29451
29525
  if (!HOP(options.format, "code") || options.format.code) {
29526
+ if (!options.format.ast) {
29527
+ // Destroy stuff to save RAM. (unless the deprecated `ast` option is on)
29528
+ options.format._destroy_ast = true;
29529
+
29530
+ walk(toplevel, node => {
29531
+ if (node instanceof AST_Scope) {
29532
+ node.variables = undefined;
29533
+ node.enclosed = undefined;
29534
+ node.parent_scope = undefined;
29535
+ }
29536
+ if (node.block_scope) {
29537
+ node.block_scope.variables = undefined;
29538
+ node.block_scope.enclosed = undefined;
29539
+ node.parent_scope = undefined;
29540
+ }
29541
+ });
29542
+ }
29543
+
29452
29544
  if (options.sourceMap) {
29545
+ if (options.sourceMap.includeSources && files instanceof AST_Toplevel) {
29546
+ throw new Error("original source content unavailable");
29547
+ }
29453
29548
  options.format.source_map = await SourceMap({
29454
29549
  file: options.sourceMap.filename,
29455
29550
  orig: options.sourceMap.content,
29456
- root: options.sourceMap.root
29551
+ root: options.sourceMap.root,
29552
+ files: options.sourceMap.includeSources ? files : null,
29457
29553
  });
29458
- if (options.sourceMap.includeSources) {
29459
- if (files instanceof AST_Toplevel) {
29460
- throw new Error("original source content unavailable");
29461
- } else for (var name in files) if (HOP(files, name)) {
29462
- options.format.source_map.get().setSourceContent(name, files[name]);
29463
- }
29464
- }
29465
29554
  }
29466
29555
  delete options.format.ast;
29467
29556
  delete options.format.code;
@@ -29470,11 +29559,21 @@ async function minify(files, options, _fs_module) {
29470
29559
  toplevel.print(stream);
29471
29560
  result.code = stream.get();
29472
29561
  if (options.sourceMap) {
29473
- if(options.sourceMap.asObject) {
29474
- result.map = options.format.source_map.get().toJSON();
29475
- } else {
29476
- result.map = options.format.source_map.toString();
29477
- }
29562
+ Object.defineProperty(result, "map", {
29563
+ configurable: true,
29564
+ enumerable: true,
29565
+ get() {
29566
+ const map = options.format.source_map.getEncoded();
29567
+ return (result.map = options.sourceMap.asObject ? map : JSON.stringify(map));
29568
+ },
29569
+ set(value) {
29570
+ Object.defineProperty(result, "map", {
29571
+ value,
29572
+ writable: true,
29573
+ });
29574
+ }
29575
+ });
29576
+ result.decoded_map = options.format.source_map.getDecoded();
29478
29577
  if (options.sourceMap.url == "inline") {
29479
29578
  var sourceMap = typeof result.map === "object" ? JSON.stringify(result.map) : result.map;
29480
29579
  result.code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + to_base64(sourceMap);
package/lib/ast.js CHANGED
@@ -93,6 +93,7 @@ const set_tok_flag = (tok, flag, truth) => {
93
93
  const TOK_FLAG_NLB = 0b0001;
94
94
  const TOK_FLAG_QUOTE_SINGLE = 0b0010;
95
95
  const TOK_FLAG_QUOTE_EXISTS = 0b0100;
96
+ const TOK_FLAG_TEMPLATE_END = 0b1000;
96
97
 
97
98
  class AST_Token {
98
99
  constructor(type, value, line, col, pos, nlb, comments_before, comments_after, file) {
@@ -128,6 +129,14 @@ class AST_Token {
128
129
  set_tok_flag(this, TOK_FLAG_QUOTE_SINGLE, quote_type === "'");
129
130
  set_tok_flag(this, TOK_FLAG_QUOTE_EXISTS, !!quote_type);
130
131
  }
132
+
133
+ get template_end() {
134
+ return has_tok_flag(this, TOK_FLAG_TEMPLATE_END);
135
+ }
136
+
137
+ set template_end(new_template_end) {
138
+ set_tok_flag(this, TOK_FLAG_TEMPLATE_END, new_template_end);
139
+ }
131
140
  }
132
141
 
133
142
  var AST_Node = DEFNODE("Node", "start end", function AST_Node(props) {
@@ -552,11 +561,10 @@ var AST_With = DEFNODE("With", "expression", function AST_With(props) {
552
561
 
553
562
  var AST_Scope = DEFNODE(
554
563
  "Scope",
555
- "variables functions uses_with uses_eval parent_scope enclosed cname",
564
+ "variables uses_with uses_eval parent_scope enclosed cname",
556
565
  function AST_Scope(props) {
557
566
  if (props) {
558
567
  this.variables = props.variables;
559
- this.functions = props.functions;
560
568
  this.uses_with = props.uses_with;
561
569
  this.uses_eval = props.uses_eval;
562
570
  this.parent_scope = props.parent_scope;
@@ -612,7 +620,6 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", function AST_Toplevel(props) {
612
620
  if (props) {
613
621
  this.globals = props.globals;
614
622
  this.variables = props.variables;
615
- this.functions = props.functions;
616
623
  this.uses_with = props.uses_with;
617
624
  this.uses_eval = props.uses_eval;
618
625
  this.parent_scope = props.parent_scope;
@@ -694,7 +701,6 @@ var AST_Lambda = DEFNODE(
694
701
  this.is_generator = props.is_generator;
695
702
  this.async = props.async;
696
703
  this.variables = props.variables;
697
- this.functions = props.functions;
698
704
  this.uses_with = props.uses_with;
699
705
  this.uses_eval = props.uses_eval;
700
706
  this.parent_scope = props.parent_scope;
@@ -774,7 +780,6 @@ var AST_Accessor = DEFNODE("Accessor", null, function AST_Accessor(props) {
774
780
  this.is_generator = props.is_generator;
775
781
  this.async = props.async;
776
782
  this.variables = props.variables;
777
- this.functions = props.functions;
778
783
  this.uses_with = props.uses_with;
779
784
  this.uses_eval = props.uses_eval;
780
785
  this.parent_scope = props.parent_scope;
@@ -799,7 +804,6 @@ var AST_Function = DEFNODE("Function", null, function AST_Function(props) {
799
804
  this.is_generator = props.is_generator;
800
805
  this.async = props.async;
801
806
  this.variables = props.variables;
802
- this.functions = props.functions;
803
807
  this.uses_with = props.uses_with;
804
808
  this.uses_eval = props.uses_eval;
805
809
  this.parent_scope = props.parent_scope;
@@ -824,7 +828,6 @@ var AST_Arrow = DEFNODE("Arrow", null, function AST_Arrow(props) {
824
828
  this.is_generator = props.is_generator;
825
829
  this.async = props.async;
826
830
  this.variables = props.variables;
827
- this.functions = props.functions;
828
831
  this.uses_with = props.uses_with;
829
832
  this.uses_eval = props.uses_eval;
830
833
  this.parent_scope = props.parent_scope;
@@ -849,7 +852,6 @@ var AST_Defun = DEFNODE("Defun", null, function AST_Defun(props) {
849
852
  this.is_generator = props.is_generator;
850
853
  this.async = props.async;
851
854
  this.variables = props.variables;
852
- this.functions = props.functions;
853
855
  this.uses_with = props.uses_with;
854
856
  this.uses_eval = props.uses_eval;
855
857
  this.parent_scope = props.parent_scope;
@@ -2161,7 +2163,6 @@ var AST_Class = DEFNODE("Class", "name extends properties", function AST_Class(p
2161
2163
  this.extends = props.extends;
2162
2164
  this.properties = props.properties;
2163
2165
  this.variables = props.variables;
2164
- this.functions = props.functions;
2165
2166
  this.uses_with = props.uses_with;
2166
2167
  this.uses_eval = props.uses_eval;
2167
2168
  this.parent_scope = props.parent_scope;
@@ -2255,7 +2256,6 @@ var AST_DefClass = DEFNODE("DefClass", null, function AST_DefClass(props) {
2255
2256
  this.extends = props.extends;
2256
2257
  this.properties = props.properties;
2257
2258
  this.variables = props.variables;
2258
- this.functions = props.functions;
2259
2259
  this.uses_with = props.uses_with;
2260
2260
  this.uses_eval = props.uses_eval;
2261
2261
  this.parent_scope = props.parent_scope;
@@ -2278,7 +2278,6 @@ var AST_ClassExpression = DEFNODE("ClassExpression", null, function AST_ClassExp
2278
2278
  this.extends = props.extends;
2279
2279
  this.properties = props.properties;
2280
2280
  this.variables = props.variables;
2281
- this.functions = props.functions;
2282
2281
  this.uses_with = props.uses_with;
2283
2282
  this.uses_eval = props.uses_eval;
2284
2283
  this.parent_scope = props.parent_scope;
@@ -46,7 +46,8 @@ import {
46
46
  makePredicate,
47
47
  return_this,
48
48
  string_template,
49
- regexp_source_fix
49
+ regexp_source_fix,
50
+ regexp_is_safe,
50
51
  } from "../utils/index.js";
51
52
  import {
52
53
  AST_Array,
@@ -129,7 +130,7 @@ def_eval(AST_BigInt, return_this);
129
130
 
130
131
  def_eval(AST_RegExp, function (compressor) {
131
132
  let evaluated = compressor.evaluated_regexps.get(this.value);
132
- if (evaluated === undefined) {
133
+ if (evaluated === undefined && regexp_is_safe(this.value.source)) {
133
134
  try {
134
135
  const { source, flags } = this.value;
135
136
  evaluated = new RegExp(source, flags);
@@ -342,7 +343,7 @@ const regexp_flags = new Set([
342
343
  ]);
343
344
 
344
345
  def_eval(AST_PropAccess, function (compressor, depth) {
345
- const obj = this.expression._eval(compressor, depth);
346
+ let obj = this.expression._eval(compressor, depth + 1);
346
347
  if (obj === nullish || (this.optional && obj == null)) return nullish;
347
348
  if (compressor.option("unsafe")) {
348
349
  var key = this.property;
@@ -352,7 +353,6 @@ def_eval(AST_PropAccess, function (compressor, depth) {
352
353
  return this;
353
354
  }
354
355
  var exp = this.expression;
355
- var val;
356
356
  if (is_undeclared_ref(exp)) {
357
357
 
358
358
  var aa;
@@ -369,29 +369,29 @@ def_eval(AST_PropAccess, function (compressor, depth) {
369
369
  }
370
370
  if (!is_pure_native_value(exp.name, key))
371
371
  return this;
372
- val = global_objs[exp.name];
372
+ obj = global_objs[exp.name];
373
373
  } else {
374
- val = exp._eval(compressor, depth + 1);
375
- if (val instanceof RegExp) {
374
+ if (obj instanceof RegExp) {
376
375
  if (key == "source") {
377
- return regexp_source_fix(val.source);
376
+ return regexp_source_fix(obj.source);
378
377
  } else if (key == "flags" || regexp_flags.has(key)) {
379
- return val[key];
378
+ return obj[key];
380
379
  }
381
380
  }
382
- if (!val || val === exp || !HOP(val, key))
381
+ if (!obj || obj === exp || !HOP(obj, key))
383
382
  return this;
384
- if (typeof val == "function")
383
+
384
+ if (typeof obj == "function")
385
385
  switch (key) {
386
386
  case "name":
387
- return val.node.name ? val.node.name.name : "";
387
+ return obj.node.name ? obj.node.name.name : "";
388
388
  case "length":
389
- return val.node.length_property();
389
+ return obj.node.length_property();
390
390
  default:
391
391
  return this;
392
392
  }
393
393
  }
394
- return val[key];
394
+ return obj[key];
395
395
  }
396
396
  return this;
397
397
  });
@@ -158,6 +158,7 @@ import {
158
158
  return_true,
159
159
  regexp_source_fix,
160
160
  has_annotation,
161
+ regexp_is_safe,
161
162
  } from "../utils/index.js";
162
163
  import { first_in_statement } from "../utils/first_in_statement.js";
163
164
  import { equivalent_to } from "../equivalent-to.js";
@@ -2140,6 +2141,7 @@ def_optimize(AST_Call, function(self, compressor) {
2140
2141
  params.push(value);
2141
2142
  return arg !== value;
2142
2143
  })
2144
+ && regexp_is_safe(params[0])
2143
2145
  ) {
2144
2146
  let [ source, flags ] = params;
2145
2147
  source = regexp_source_fix(new RegExp(source).source);
@@ -3799,6 +3801,12 @@ def_optimize(AST_Dot, function(self, compressor) {
3799
3801
  const sub = self.flatten_object(self.property, compressor);
3800
3802
  if (sub) return sub.optimize(compressor);
3801
3803
  }
3804
+
3805
+ if (self.expression instanceof AST_PropAccess
3806
+ && parent instanceof AST_PropAccess) {
3807
+ return self;
3808
+ }
3809
+
3802
3810
  let ev = self.evaluate(compressor);
3803
3811
  if (ev !== self) {
3804
3812
  ev = make_node_from_constant(ev, self).optimize(compressor);
package/lib/minify.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  map_to_object,
8
8
  HOP,
9
9
  } from "./utils/index.js";
10
- import { AST_Toplevel, AST_Node } from "./ast.js";
10
+ import { AST_Toplevel, AST_Node, walk, AST_Scope } from "./ast.js";
11
11
  import { parse } from "./parse.js";
12
12
  import { OutputStream } from "./output.js";
13
13
  import { Compressor } from "./compress/index.js";
@@ -194,6 +194,8 @@ async function minify(files, options, _fs_module) {
194
194
  url: null,
195
195
  }, true);
196
196
  }
197
+
198
+ // -- Parse phase --
197
199
  if (timings) timings.parse = Date.now();
198
200
  var toplevel;
199
201
  if (files instanceof AST_Toplevel) {
@@ -243,12 +245,16 @@ async function minify(files, options, _fs_module) {
243
245
  toplevel.figure_out_scope(options.mangle);
244
246
  toplevel.expand_names(options.mangle);
245
247
  }
248
+
249
+ // -- Compress phase --
246
250
  if (timings) timings.compress = Date.now();
247
251
  if (options.compress) {
248
252
  toplevel = new Compressor(options.compress, {
249
253
  mangle_options: options.mangle
250
254
  }).compress(toplevel);
251
255
  }
256
+
257
+ // -- Mangle phase --
252
258
  if (timings) timings.scope = Date.now();
253
259
  if (options.mangle) toplevel.figure_out_scope(options.mangle);
254
260
  if (timings) timings.mangle = Date.now();
@@ -261,6 +267,8 @@ async function minify(files, options, _fs_module) {
261
267
  if (options.mangle && options.mangle.properties) {
262
268
  toplevel = mangle_properties(toplevel, options.mangle.properties);
263
269
  }
270
+
271
+ // Format phase
264
272
  if (timings) timings.format = Date.now();
265
273
  var result = {};
266
274
  if (options.format.ast) {
@@ -270,19 +278,34 @@ async function minify(files, options, _fs_module) {
270
278
  result.ast = toplevel.to_mozilla_ast();
271
279
  }
272
280
  if (!HOP(options.format, "code") || options.format.code) {
281
+ if (!options.format.ast) {
282
+ // Destroy stuff to save RAM. (unless the deprecated `ast` option is on)
283
+ options.format._destroy_ast = true;
284
+
285
+ walk(toplevel, node => {
286
+ if (node instanceof AST_Scope) {
287
+ node.variables = undefined;
288
+ node.enclosed = undefined;
289
+ node.parent_scope = undefined;
290
+ }
291
+ if (node.block_scope) {
292
+ node.block_scope.variables = undefined;
293
+ node.block_scope.enclosed = undefined;
294
+ node.parent_scope = undefined;
295
+ }
296
+ });
297
+ }
298
+
273
299
  if (options.sourceMap) {
300
+ if (options.sourceMap.includeSources && files instanceof AST_Toplevel) {
301
+ throw new Error("original source content unavailable");
302
+ }
274
303
  options.format.source_map = await SourceMap({
275
304
  file: options.sourceMap.filename,
276
305
  orig: options.sourceMap.content,
277
- root: options.sourceMap.root
306
+ root: options.sourceMap.root,
307
+ files: options.sourceMap.includeSources ? files : null,
278
308
  });
279
- if (options.sourceMap.includeSources) {
280
- if (files instanceof AST_Toplevel) {
281
- throw new Error("original source content unavailable");
282
- } else for (var name in files) if (HOP(files, name)) {
283
- options.format.source_map.get().setSourceContent(name, files[name]);
284
- }
285
- }
286
309
  }
287
310
  delete options.format.ast;
288
311
  delete options.format.code;
@@ -291,11 +314,21 @@ async function minify(files, options, _fs_module) {
291
314
  toplevel.print(stream);
292
315
  result.code = stream.get();
293
316
  if (options.sourceMap) {
294
- if(options.sourceMap.asObject) {
295
- result.map = options.format.source_map.get().toJSON();
296
- } else {
297
- result.map = options.format.source_map.toString();
298
- }
317
+ Object.defineProperty(result, "map", {
318
+ configurable: true,
319
+ enumerable: true,
320
+ get() {
321
+ const map = options.format.source_map.getEncoded();
322
+ return (result.map = options.sourceMap.asObject ? map : JSON.stringify(map));
323
+ },
324
+ set(value) {
325
+ Object.defineProperty(result, "map", {
326
+ value,
327
+ writable: true,
328
+ });
329
+ }
330
+ });
331
+ result.decoded_map = options.format.source_map.getDecoded();
299
332
  if (options.sourceMap.url == "inline") {
300
333
  var sourceMap = typeof result.map === "object" ? JSON.stringify(result.map) : result.map;
301
334
  result.code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + to_base64(sourceMap);
package/lib/output.js CHANGED
@@ -247,6 +247,8 @@ function OutputStream(options) {
247
247
  width : 80,
248
248
  wrap_iife : false,
249
249
  wrap_func_args : true,
250
+
251
+ _destroy_ast : false
250
252
  }, true);
251
253
 
252
254
  if (options.shorthand === undefined)
@@ -796,6 +798,18 @@ function OutputStream(options) {
796
798
  if (OUTPUT.length() > insert) newline_insert = insert;
797
799
  }
798
800
 
801
+ /**
802
+ * When output.option("_destroy_ast") is enabled, destroy the function.
803
+ * Call this after printing it.
804
+ */
805
+ const gc_scope =
806
+ options["_destroy_ast"]
807
+ ? function gc_scope(scope) {
808
+ scope.body.length = 0;
809
+ scope.argnames.length = 0;
810
+ }
811
+ : noop;
812
+
799
813
  var stack = [];
800
814
  return {
801
815
  get : get,
@@ -842,6 +856,7 @@ function OutputStream(options) {
842
856
  with_square : with_square,
843
857
  add_mapping : add_mapping,
844
858
  option : function(opt) { return options[opt]; },
859
+ gc_scope,
845
860
  printed_comments: printed_comments,
846
861
  prepend_comments: readonly ? noop : prepend_comments,
847
862
  append_comments : readonly || comment_filter === return_false ? noop : append_comments,
@@ -1232,12 +1247,14 @@ function OutputStream(options) {
1232
1247
  output.with_indent(output.next_indent(), function() {
1233
1248
  output.append_comments(self, true);
1234
1249
  });
1250
+ output.add_mapping(self.end);
1235
1251
  output.print("}");
1236
1252
  }
1237
1253
  function print_braced(self, output, allow_directives) {
1238
1254
  if (self.body.length > 0) {
1239
1255
  output.with_block(function() {
1240
1256
  display_body(self.body, false, output, allow_directives);
1257
+ output.add_mapping(self.end);
1241
1258
  });
1242
1259
  } else print_braced_empty(self, output);
1243
1260
  }
@@ -1358,6 +1375,7 @@ function OutputStream(options) {
1358
1375
  });
1359
1376
  DEFPRINT(AST_Lambda, function(self, output) {
1360
1377
  self._do_print(output);
1378
+ output.gc_scope(self);
1361
1379
  });
1362
1380
 
1363
1381
  DEFPRINT(AST_PrefixedTemplateString, function(self, output) {
@@ -1437,6 +1455,7 @@ function OutputStream(options) {
1437
1455
  print_braced(self, output);
1438
1456
  }
1439
1457
  if (needs_parens) { output.print(")"); }
1458
+ output.gc_scope(self);
1440
1459
  });
1441
1460
 
1442
1461
  /* -----[ exits ]----- */
package/lib/parse.js CHANGED
@@ -162,7 +162,7 @@ import {
162
162
  } from "./ast.js";
163
163
 
164
164
  var LATEST_RAW = ""; // Only used for numbers and template strings
165
- var LATEST_TEMPLATE_END = true;
165
+ var TEMPLATE_RAWS = new Map(); // Raw template strings
166
166
 
167
167
  var KEYWORDS = "break case catch class const continue debugger default delete do else export extends finally for function if in instanceof let new return switch throw try typeof var void while with";
168
168
  var KEYWORDS_ATOM = "false null true";
@@ -693,8 +693,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
693
693
  next(true, true);
694
694
  S.brace_counter++;
695
695
  tok = token(begin ? "template_head" : "template_substitution", content);
696
- LATEST_RAW = raw;
697
- LATEST_TEMPLATE_END = false;
696
+ TEMPLATE_RAWS.set(tok, raw);
697
+ tok.template_end = false;
698
698
  return tok;
699
699
  }
700
700
 
@@ -710,8 +710,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
710
710
  }
711
711
  S.template_braces.pop();
712
712
  tok = token(begin ? "template_head" : "template_substitution", content);
713
- LATEST_RAW = raw;
714
- LATEST_TEMPLATE_END = true;
713
+ TEMPLATE_RAWS.set(tok, raw);
714
+ tok.template_end = true;
715
715
  return tok;
716
716
  });
717
717
 
@@ -2367,19 +2367,19 @@ function parse($TEXT, options) {
2367
2367
 
2368
2368
  segments.push(new AST_TemplateSegment({
2369
2369
  start: S.token,
2370
- raw: LATEST_RAW,
2370
+ raw: TEMPLATE_RAWS.get(S.token),
2371
2371
  value: S.token.value,
2372
2372
  end: S.token
2373
2373
  }));
2374
2374
 
2375
- while (!LATEST_TEMPLATE_END) {
2375
+ while (!S.token.template_end) {
2376
2376
  next();
2377
2377
  handle_regexp();
2378
2378
  segments.push(expression(true));
2379
2379
 
2380
2380
  segments.push(new AST_TemplateSegment({
2381
2381
  start: S.token,
2382
- raw: LATEST_RAW,
2382
+ raw: TEMPLATE_RAWS.get(S.token),
2383
2383
  value: S.token.value,
2384
2384
  end: S.token
2385
2385
  }));
@@ -3342,6 +3342,7 @@ function parse($TEXT, options) {
3342
3342
  } else {
3343
3343
  toplevel = new AST_Toplevel({ start: start, body: body, end: end });
3344
3344
  }
3345
+ TEMPLATE_RAWS = new Map();
3345
3346
  return toplevel;
3346
3347
  })();
3347
3348
 
package/lib/sourcemap.js CHANGED
@@ -43,45 +43,59 @@
43
43
 
44
44
  "use strict";
45
45
 
46
- import MOZ_SourceMap from "source-map";
47
- import {
48
- defaults,
49
- } from "./utils/index.js";
46
+ import {SourceMapConsumer, SourceMapGenerator} from "@jridgewell/source-map";
47
+ import {defaults, HOP} from "./utils/index.js";
50
48
 
51
- // a small wrapper around fitzgen's source-map library
49
+ // a small wrapper around source-map and @jridgewell/source-map
52
50
  async function SourceMap(options) {
53
51
  options = defaults(options, {
54
52
  file : null,
55
53
  root : null,
56
54
  orig : null,
57
-
58
- orig_line_diff : 0,
59
- dest_line_diff : 0,
55
+ files: {},
60
56
  });
61
57
 
62
58
  var orig_map;
63
- var generator = new MOZ_SourceMap.SourceMapGenerator({
59
+ var generator = new SourceMapGenerator({
64
60
  file : options.file,
65
61
  sourceRoot : options.root
66
62
  });
67
63
 
64
+ let sourcesContent = {__proto__: null};
65
+ let files = options.files;
66
+ for (var name in files) if (HOP(files, name)) {
67
+ sourcesContent[name] = files[name];
68
+ }
68
69
  if (options.orig) {
69
- orig_map = await new MOZ_SourceMap.SourceMapConsumer(options.orig);
70
- orig_map.sources.forEach(function(source) {
71
- var sourceContent = orig_map.sourceContentFor(source, true);
72
- if (sourceContent) {
73
- generator.setSourceContent(source, sourceContent);
74
- }
75
- });
70
+ // We support both @jridgewell/source-map (which has a sync
71
+ // SourceMapConsumer) and source-map (which has an async
72
+ // SourceMapConsumer).
73
+ orig_map = await new SourceMapConsumer(options.orig);
74
+ if (orig_map.sourcesContent) {
75
+ orig_map.sources.forEach(function(source, i) {
76
+ var content = orig_map.sourcesContent[i];
77
+ if (content) {
78
+ sourcesContent[source] = content;
79
+ }
80
+ });
81
+ }
76
82
  }
77
83
 
78
84
  function add(source, gen_line, gen_col, orig_line, orig_col, name) {
85
+ let generatedPos = { line: gen_line, column: gen_col };
86
+
79
87
  if (orig_map) {
80
88
  var info = orig_map.originalPositionFor({
81
89
  line: orig_line,
82
90
  column: orig_col
83
91
  });
84
92
  if (info.source === null) {
93
+ generator.addMapping({
94
+ generated: generatedPos,
95
+ original: null,
96
+ source: null,
97
+ name: null
98
+ });
85
99
  return;
86
100
  }
87
101
  source = info.source;
@@ -90,22 +104,42 @@ async function SourceMap(options) {
90
104
  name = info.name || name;
91
105
  }
92
106
  generator.addMapping({
93
- generated : { line: gen_line + options.dest_line_diff, column: gen_col },
94
- original : { line: orig_line + options.orig_line_diff, column: orig_col },
107
+ generated : generatedPos,
108
+ original : { line: orig_line, column: orig_col },
95
109
  source : source,
96
110
  name : name
97
111
  });
112
+ generator.setSourceContent(source, sourcesContent[source]);
113
+ }
114
+
115
+ function clean(map) {
116
+ const allNull = map.sourcesContent && map.sourcesContent.every(c => c == null);
117
+ if (allNull) delete map.sourcesContent;
118
+ if (map.file === undefined) delete map.file;
119
+ if (map.sourceRoot === undefined) delete map.sourceRoot;
120
+ return map;
121
+ }
122
+
123
+ function getDecoded() {
124
+ if (!generator.toDecodedMap) return null;
125
+ return clean(generator.toDecodedMap());
126
+ }
127
+
128
+ function getEncoded() {
129
+ return clean(generator.toJSON());
130
+ }
131
+
132
+ function destroy() {
133
+ // @jridgewell/source-map's SourceMapConsumer does not need to be
134
+ // manually freed.
135
+ if (orig_map && orig_map.destroy) orig_map.destroy();
98
136
  }
99
137
 
100
138
  return {
101
- add : add,
102
- get : function() { return generator; },
103
- toString : function() { return generator.toString(); },
104
- destroy : function () {
105
- if (orig_map && orig_map.destroy) {
106
- orig_map.destroy();
107
- }
108
- }
139
+ add,
140
+ getDecoded,
141
+ getEncoded,
142
+ destroy,
109
143
  };
110
144
  }
111
145
 
@@ -249,7 +249,15 @@ function regexp_source_fix(source) {
249
249
  return (escaped ? "" : "\\") + lineTerminatorEscape[match];
250
250
  });
251
251
  }
252
- const all_flags = "gimuy";
252
+
253
+ // Subset of regexps that is not going to cause regexp based DDOS
254
+ // https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
255
+ const re_safe_regexp = /^[\\/|\0\s\w^$.[\]()]*$/;
256
+
257
+ /** Check if the regexp is safe for Terser to create without risking a RegExp DOS */
258
+ export const regexp_is_safe = (source) => re_safe_regexp.test(source);
259
+
260
+ const all_flags = "dgimsuy";
253
261
  function sort_regexp_flags(flags) {
254
262
  const existing_flags = new Set(flags.split(""));
255
263
  let out = "";
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "homepage": "https://terser.org",
5
5
  "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
6
6
  "license": "BSD-2-Clause",
7
- "version": "5.13.1",
7
+ "version": "5.14.2",
8
8
  "engines": {
9
9
  "node": ">=10"
10
10
  },
@@ -18,6 +18,7 @@
18
18
  "exports": {
19
19
  ".": [
20
20
  {
21
+ "types": "./tools/terser.d.ts",
21
22
  "import": "./main.js",
22
23
  "require": "./dist/bundle.min.js"
23
24
  },
@@ -43,9 +44,9 @@
43
44
  "main.js"
44
45
  ],
45
46
  "dependencies": {
47
+ "@jridgewell/source-map": "^0.3.2",
46
48
  "acorn": "^8.5.0",
47
49
  "commander": "^2.20.0",
48
- "source-map": "~0.8.0-beta.0",
49
50
  "source-map-support": "~0.5.20"
50
51
  },
51
52
  "devDependencies": {
@@ -58,7 +59,8 @@
58
59
  "pre-commit": "^1.2.2",
59
60
  "rimraf": "^3.0.2",
60
61
  "rollup": "2.56.3",
61
- "semver": "^7.3.4"
62
+ "semver": "^7.3.4",
63
+ "source-map": "~0.8.0-beta.0"
62
64
  },
63
65
  "scripts": {
64
66
  "test": "node test/compress.js && mocha test/mocha",
package/tools/domprops.js CHANGED
@@ -320,6 +320,7 @@ export var domprops = [
320
320
  "COMMENT_NODE",
321
321
  "COMPARE_REF_TO_TEXTURE",
322
322
  "COMPILE_STATUS",
323
+ "COMPLETION_STATUS_KHR",
323
324
  "COMPRESSED_RGBA_S3TC_DXT1_EXT",
324
325
  "COMPRESSED_RGBA_S3TC_DXT3_EXT",
325
326
  "COMPRESSED_RGBA_S3TC_DXT5_EXT",
package/tools/terser.d.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  /// <reference lib="es2015" />
2
2
 
3
- import { RawSourceMap } from 'source-map';
3
+ import { SectionedSourceMapInput, EncodedSourceMap, DecodedSourceMap } from '@jridgewell/source-map';
4
4
 
5
5
  export type ECMA = 5 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020;
6
6
 
7
7
  export interface ParseOptions {
8
8
  bare_returns?: boolean;
9
+ /** @deprecated legacy option. Currently, all supported EcmaScript is valid to parse. */
9
10
  ecma?: ECMA;
10
11
  html5_comments?: boolean;
11
12
  shebang?: boolean;
@@ -144,6 +145,7 @@ export interface FormatOptions {
144
145
  }) => boolean );
145
146
  ecma?: ECMA;
146
147
  ie8?: boolean;
148
+ keep_numbers?: boolean;
147
149
  indent_level?: number;
148
150
  indent_start?: number;
149
151
  inline_script?: boolean;
@@ -192,12 +194,13 @@ export interface MinifyOptions {
192
194
 
193
195
  export interface MinifyOutput {
194
196
  code?: string;
195
- map?: RawSourceMap | string;
197
+ map?: EncodedSourceMap | string;
198
+ decoded_map?: DecodedSourceMap | null;
196
199
  }
197
200
 
198
201
  export interface SourceMapOptions {
199
202
  /** Source map object, 'inline' or source map file content */
200
- content?: RawSourceMap | string;
203
+ content?: SectionedSourceMapInput | string;
201
204
  includeSources?: boolean;
202
205
  filename?: string;
203
206
  root?: string;