From 46e1eaaaf05e0c2efa0ed395d7a3cfa99ef25fb5 Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Sun, 16 Oct 2022 06:42:39 +0200 Subject: [PATCH] Preserve rendered class names (#4674) * Preserve class declaration names * Preserve class expression names * Slightly improve code --- src/ast/nodes/ClassDeclaration.ts | 17 +++++++++-- src/ast/nodes/VariableDeclarator.ts | 28 ++++++++++++------- .../_expected/amd/generated-main1.js | 4 +-- .../_expected/cjs/generated-main1.js | 4 +-- .../_expected/es/generated-main1.js | 4 +-- .../_expected/system/generated-main1.js | 4 +-- .../_expected/amd/main2.js | 4 +-- .../_expected/cjs/main2.js | 4 +-- .../_expected/es/main2.js | 4 +-- .../_expected/system/main2.js | 4 +-- .../_expected/amd/generated-main1.js | 4 +-- .../_expected/cjs/generated-main1.js | 4 +-- .../_expected/es/generated-main1.js | 4 +-- .../_expected/system/generated-main1.js | 4 +-- .../_config.js | 4 +++ .../_expected/amd.js | 10 +++++++ .../_expected/cjs.js | 8 ++++++ .../_expected/es.js | 5 ++++ .../_expected/iife.js | 13 +++++++++ .../_expected/system.js | 12 ++++++++ .../_expected/umd.js | 14 ++++++++++ .../first.js | 2 ++ .../main.js | 2 ++ .../second.js | 2 ++ .../samples/class-name-conflict/_config.js | 3 ++ .../class-name-conflict/declaration1.js | 3 ++ .../class-name-conflict/declaration2.js | 3 ++ .../class-name-conflict/expression1.js | 3 ++ .../class-name-conflict/expression2.js | 3 ++ .../samples/class-name-conflict/main.js | 4 +++ 30 files changed, 147 insertions(+), 37 deletions(-) create mode 100644 test/form/samples/exported-class-declaration-conflict/_config.js create mode 100644 test/form/samples/exported-class-declaration-conflict/_expected/amd.js create mode 100644 test/form/samples/exported-class-declaration-conflict/_expected/cjs.js create mode 100644 test/form/samples/exported-class-declaration-conflict/_expected/es.js create mode 100644 test/form/samples/exported-class-declaration-conflict/_expected/iife.js create mode 100644 test/form/samples/exported-class-declaration-conflict/_expected/system.js create mode 100644 test/form/samples/exported-class-declaration-conflict/_expected/umd.js create mode 100644 test/form/samples/exported-class-declaration-conflict/first.js create mode 100644 test/form/samples/exported-class-declaration-conflict/main.js create mode 100644 test/form/samples/exported-class-declaration-conflict/second.js create mode 100644 test/function/samples/class-name-conflict/_config.js create mode 100644 test/function/samples/class-name-conflict/declaration1.js create mode 100644 test/function/samples/class-name-conflict/declaration2.js create mode 100644 test/function/samples/class-name-conflict/expression1.js create mode 100644 test/function/samples/class-name-conflict/expression2.js create mode 100644 test/function/samples/class-name-conflict/main.js diff --git a/src/ast/nodes/ClassDeclaration.ts b/src/ast/nodes/ClassDeclaration.ts index 589f866b6f2..6766be702fb 100644 --- a/src/ast/nodes/ClassDeclaration.ts +++ b/src/ast/nodes/ClassDeclaration.ts @@ -33,10 +33,21 @@ export default class ClassDeclaration extends ClassNode { const { exportNamesByVariable, format, - snippets: { _ } + snippets: { _, getPropertyAccess } } = options; - if (format === 'system' && this.id && exportNamesByVariable.has(this.id.variable)) { - code.appendLeft(this.end, `${_}${getSystemExportStatement([this.id.variable], options)};`); + if (this.id) { + const { variable, name } = this.id; + if (format === 'system' && exportNamesByVariable.has(variable)) { + code.appendLeft(this.end, `${_}${getSystemExportStatement([variable], options)};`); + } + const renderedVariable = variable.getName(getPropertyAccess); + if (renderedVariable !== name) { + this.superClass?.render(code, options); + this.body.render(code, options); + code.prependRight(this.start, `let ${renderedVariable}${_}=${_}`); + code.prependLeft(this.end, ';'); + return; + } } super.render(code, options); } diff --git a/src/ast/nodes/VariableDeclarator.ts b/src/ast/nodes/VariableDeclarator.ts index 2ffef75cd92..6259514f0ed 100644 --- a/src/ast/nodes/VariableDeclarator.ts +++ b/src/ast/nodes/VariableDeclarator.ts @@ -9,6 +9,7 @@ import { import type { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import type { ObjectPath } from '../utils/PathTracker'; import { UNDEFINED_EXPRESSION } from '../values'; +import ClassExpression from './ClassExpression'; import Identifier from './Identifier'; import * as NodeType from './NodeType'; import { type ExpressionNode, type IncludeChildren, NodeBase } from './shared/Node'; @@ -45,26 +46,33 @@ export default class VariableDeclarator extends NodeBase { render(code: MagicString, options: RenderOptions): void { const { exportNamesByVariable, - snippets: { _ } + snippets: { _, getPropertyAccess } } = options; - const renderId = this.id.included; + const { end, id, init, start } = this; + const renderId = id.included; if (renderId) { - this.id.render(code, options); + id.render(code, options); } else { - const operatorPos = findFirstOccurrenceOutsideComment(code.original, '=', this.id.end); - code.remove(this.start, findNonWhiteSpace(code.original, operatorPos + 1)); + const operatorPos = findFirstOccurrenceOutsideComment(code.original, '=', id.end); + code.remove(start, findNonWhiteSpace(code.original, operatorPos + 1)); } - if (this.init) { - this.init.render( + if (init) { + if (id instanceof Identifier && init instanceof ClassExpression && !init.id) { + const renderedVariable = id.variable!.getName(getPropertyAccess); + if (renderedVariable !== id.name) { + code.appendLeft(init.start + 5, ` ${id.name}`); + } + } + init.render( code, options, renderId ? BLANK : { renderedSurroundingElement: NodeType.ExpressionStatement } ); } else if ( - this.id instanceof Identifier && - isReassignedExportsMember(this.id.variable!, exportNamesByVariable) + id instanceof Identifier && + isReassignedExportsMember(id.variable!, exportNamesByVariable) ) { - code.appendLeft(this.end, `${_}=${_}void 0`); + code.appendLeft(end, `${_}=${_}void 0`); } } diff --git a/test/chunking-form/samples/circular-entry-points/_expected/amd/generated-main1.js b/test/chunking-form/samples/circular-entry-points/_expected/amd/generated-main1.js index 58149846e1f..fd709c6671b 100644 --- a/test/chunking-form/samples/circular-entry-points/_expected/amd/generated-main1.js +++ b/test/chunking-form/samples/circular-entry-points/_expected/amd/generated-main1.js @@ -1,10 +1,10 @@ define(['exports'], (function (exports) { 'use strict'; - class C$1 { + let C$1 = class C { fn (num) { console.log(num - p); } - } + }; var p$1 = 43; diff --git a/test/chunking-form/samples/circular-entry-points/_expected/cjs/generated-main1.js b/test/chunking-form/samples/circular-entry-points/_expected/cjs/generated-main1.js index a930fd89967..4b6651abf50 100644 --- a/test/chunking-form/samples/circular-entry-points/_expected/cjs/generated-main1.js +++ b/test/chunking-form/samples/circular-entry-points/_expected/cjs/generated-main1.js @@ -1,10 +1,10 @@ 'use strict'; -class C$1 { +let C$1 = class C { fn (num) { console.log(num - p); } -} +}; var p$1 = 43; diff --git a/test/chunking-form/samples/circular-entry-points/_expected/es/generated-main1.js b/test/chunking-form/samples/circular-entry-points/_expected/es/generated-main1.js index 27379cf813d..f8011f29482 100644 --- a/test/chunking-form/samples/circular-entry-points/_expected/es/generated-main1.js +++ b/test/chunking-form/samples/circular-entry-points/_expected/es/generated-main1.js @@ -1,8 +1,8 @@ -class C$1 { +let C$1 = class C { fn (num) { console.log(num - p); } -} +}; var p$1 = 43; diff --git a/test/chunking-form/samples/circular-entry-points/_expected/system/generated-main1.js b/test/chunking-form/samples/circular-entry-points/_expected/system/generated-main1.js index d35b667e2b4..d0233a23e95 100644 --- a/test/chunking-form/samples/circular-entry-points/_expected/system/generated-main1.js +++ b/test/chunking-form/samples/circular-entry-points/_expected/system/generated-main1.js @@ -3,11 +3,11 @@ System.register([], (function (exports) { return { execute: (function () { - class C$1 { + let C$1 = class C { fn (num) { console.log(num - p); } - } + }; var p$1 = exports('p', 43); diff --git a/test/chunking-form/samples/circular-entry-points2/_expected/amd/main2.js b/test/chunking-form/samples/circular-entry-points2/_expected/amd/main2.js index 6cbe142452c..2abda26ae68 100644 --- a/test/chunking-form/samples/circular-entry-points2/_expected/amd/main2.js +++ b/test/chunking-form/samples/circular-entry-points2/_expected/amd/main2.js @@ -1,10 +1,10 @@ define(['exports'], (function (exports) { 'use strict'; - class C$1 { + let C$1 = class C { fn (num) { console.log(num - p); } - } + }; var p$1 = 43; diff --git a/test/chunking-form/samples/circular-entry-points2/_expected/cjs/main2.js b/test/chunking-form/samples/circular-entry-points2/_expected/cjs/main2.js index 3b79ebd98ca..9b0f760095c 100644 --- a/test/chunking-form/samples/circular-entry-points2/_expected/cjs/main2.js +++ b/test/chunking-form/samples/circular-entry-points2/_expected/cjs/main2.js @@ -1,10 +1,10 @@ 'use strict'; -class C$1 { +let C$1 = class C { fn (num) { console.log(num - p); } -} +}; var p$1 = 43; diff --git a/test/chunking-form/samples/circular-entry-points2/_expected/es/main2.js b/test/chunking-form/samples/circular-entry-points2/_expected/es/main2.js index e4ea2f61743..ac842dd5d95 100644 --- a/test/chunking-form/samples/circular-entry-points2/_expected/es/main2.js +++ b/test/chunking-form/samples/circular-entry-points2/_expected/es/main2.js @@ -1,8 +1,8 @@ -class C$1 { +let C$1 = class C { fn (num) { console.log(num - p); } -} +}; var p$1 = 43; diff --git a/test/chunking-form/samples/circular-entry-points2/_expected/system/main2.js b/test/chunking-form/samples/circular-entry-points2/_expected/system/main2.js index b5d1de767e4..be288598d84 100644 --- a/test/chunking-form/samples/circular-entry-points2/_expected/system/main2.js +++ b/test/chunking-form/samples/circular-entry-points2/_expected/system/main2.js @@ -3,11 +3,11 @@ System.register([], (function (exports) { return { execute: (function () { - class C$1 { + let C$1 = class C { fn (num) { console.log(num - p); } - } + }; var p$1 = exports('p', 43); diff --git a/test/chunking-form/samples/circular-entry-points3/_expected/amd/generated-main1.js b/test/chunking-form/samples/circular-entry-points3/_expected/amd/generated-main1.js index 32ce53a00b5..af7cf1b5669 100644 --- a/test/chunking-form/samples/circular-entry-points3/_expected/amd/generated-main1.js +++ b/test/chunking-form/samples/circular-entry-points3/_expected/amd/generated-main1.js @@ -1,10 +1,10 @@ define(['exports'], (function (exports) { 'use strict'; - class C$1 { + let C$1 = class C { fn (num) { console.log(num - p); } - } + }; var p$1 = 43; diff --git a/test/chunking-form/samples/circular-entry-points3/_expected/cjs/generated-main1.js b/test/chunking-form/samples/circular-entry-points3/_expected/cjs/generated-main1.js index e1ec1ce79e3..bbe77088054 100644 --- a/test/chunking-form/samples/circular-entry-points3/_expected/cjs/generated-main1.js +++ b/test/chunking-form/samples/circular-entry-points3/_expected/cjs/generated-main1.js @@ -1,10 +1,10 @@ 'use strict'; -class C$1 { +let C$1 = class C { fn (num) { console.log(num - p); } -} +}; var p$1 = 43; diff --git a/test/chunking-form/samples/circular-entry-points3/_expected/es/generated-main1.js b/test/chunking-form/samples/circular-entry-points3/_expected/es/generated-main1.js index d31e8c0e1f9..bc928813a4c 100644 --- a/test/chunking-form/samples/circular-entry-points3/_expected/es/generated-main1.js +++ b/test/chunking-form/samples/circular-entry-points3/_expected/es/generated-main1.js @@ -1,8 +1,8 @@ -class C$1 { +let C$1 = class C { fn (num) { console.log(num - p); } -} +}; var p$1 = 43; diff --git a/test/chunking-form/samples/circular-entry-points3/_expected/system/generated-main1.js b/test/chunking-form/samples/circular-entry-points3/_expected/system/generated-main1.js index bc3aff572c0..800f588dc71 100644 --- a/test/chunking-form/samples/circular-entry-points3/_expected/system/generated-main1.js +++ b/test/chunking-form/samples/circular-entry-points3/_expected/system/generated-main1.js @@ -3,11 +3,11 @@ System.register([], (function (exports) { return { execute: (function () { - class C$1 { + let C$1 = class C { fn (num) { console.log(num - p); } - } exports('C', C$1); + }; exports('C', C$1); var p$1 = exports('p', 43); diff --git a/test/form/samples/exported-class-declaration-conflict/_config.js b/test/form/samples/exported-class-declaration-conflict/_config.js new file mode 100644 index 00000000000..3c601ee8f95 --- /dev/null +++ b/test/form/samples/exported-class-declaration-conflict/_config.js @@ -0,0 +1,4 @@ +module.exports = { + description: 'handles exporting class declarations with name conflicts in SystemJS', + options: { output: { name: 'bundle' } } +}; diff --git a/test/form/samples/exported-class-declaration-conflict/_expected/amd.js b/test/form/samples/exported-class-declaration-conflict/_expected/amd.js new file mode 100644 index 00000000000..451650f9385 --- /dev/null +++ b/test/form/samples/exported-class-declaration-conflict/_expected/amd.js @@ -0,0 +1,10 @@ +define(['exports'], (function (exports) { 'use strict'; + + let Foo$1 = class Foo {}; + + class Foo {} + + exports.First = Foo$1; + exports.Second = Foo; + +})); diff --git a/test/form/samples/exported-class-declaration-conflict/_expected/cjs.js b/test/form/samples/exported-class-declaration-conflict/_expected/cjs.js new file mode 100644 index 00000000000..b78094b81ce --- /dev/null +++ b/test/form/samples/exported-class-declaration-conflict/_expected/cjs.js @@ -0,0 +1,8 @@ +'use strict'; + +let Foo$1 = class Foo {}; + +class Foo {} + +exports.First = Foo$1; +exports.Second = Foo; diff --git a/test/form/samples/exported-class-declaration-conflict/_expected/es.js b/test/form/samples/exported-class-declaration-conflict/_expected/es.js new file mode 100644 index 00000000000..e64a800dc17 --- /dev/null +++ b/test/form/samples/exported-class-declaration-conflict/_expected/es.js @@ -0,0 +1,5 @@ +let Foo$1 = class Foo {}; + +class Foo {} + +export { Foo$1 as First, Foo as Second }; diff --git a/test/form/samples/exported-class-declaration-conflict/_expected/iife.js b/test/form/samples/exported-class-declaration-conflict/_expected/iife.js new file mode 100644 index 00000000000..7e31b50ef39 --- /dev/null +++ b/test/form/samples/exported-class-declaration-conflict/_expected/iife.js @@ -0,0 +1,13 @@ +var bundle = (function (exports) { + 'use strict'; + + let Foo$1 = class Foo {}; + + class Foo {} + + exports.First = Foo$1; + exports.Second = Foo; + + return exports; + +})({}); diff --git a/test/form/samples/exported-class-declaration-conflict/_expected/system.js b/test/form/samples/exported-class-declaration-conflict/_expected/system.js new file mode 100644 index 00000000000..4f9051a4704 --- /dev/null +++ b/test/form/samples/exported-class-declaration-conflict/_expected/system.js @@ -0,0 +1,12 @@ +System.register('bundle', [], (function (exports) { + 'use strict'; + return { + execute: (function () { + + let Foo$1 = class Foo {}; exports('First', Foo$1); + + class Foo {} exports('Second', Foo); + + }) + }; +})); diff --git a/test/form/samples/exported-class-declaration-conflict/_expected/umd.js b/test/form/samples/exported-class-declaration-conflict/_expected/umd.js new file mode 100644 index 00000000000..a3e0900a821 --- /dev/null +++ b/test/form/samples/exported-class-declaration-conflict/_expected/umd.js @@ -0,0 +1,14 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.bundle = {})); +})(this, (function (exports) { 'use strict'; + + let Foo$1 = class Foo {}; + + class Foo {} + + exports.First = Foo$1; + exports.Second = Foo; + +})); diff --git a/test/form/samples/exported-class-declaration-conflict/first.js b/test/form/samples/exported-class-declaration-conflict/first.js new file mode 100644 index 00000000000..a5ae7c9e1cd --- /dev/null +++ b/test/form/samples/exported-class-declaration-conflict/first.js @@ -0,0 +1,2 @@ +class Foo {} +export { Foo }; diff --git a/test/form/samples/exported-class-declaration-conflict/main.js b/test/form/samples/exported-class-declaration-conflict/main.js new file mode 100644 index 00000000000..dc165351514 --- /dev/null +++ b/test/form/samples/exported-class-declaration-conflict/main.js @@ -0,0 +1,2 @@ +export { Foo as First } from './first'; +export { Foo as Second } from './second'; diff --git a/test/form/samples/exported-class-declaration-conflict/second.js b/test/form/samples/exported-class-declaration-conflict/second.js new file mode 100644 index 00000000000..a5ae7c9e1cd --- /dev/null +++ b/test/form/samples/exported-class-declaration-conflict/second.js @@ -0,0 +1,2 @@ +class Foo {} +export { Foo }; diff --git a/test/function/samples/class-name-conflict/_config.js b/test/function/samples/class-name-conflict/_config.js new file mode 100644 index 00000000000..336557d03a1 --- /dev/null +++ b/test/function/samples/class-name-conflict/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'preserves class names even if the class is renamed' +}; diff --git a/test/function/samples/class-name-conflict/declaration1.js b/test/function/samples/class-name-conflict/declaration1.js new file mode 100644 index 00000000000..ea4c231065c --- /dev/null +++ b/test/function/samples/class-name-conflict/declaration1.js @@ -0,0 +1,3 @@ +class foo {} + +assert.strictEqual(foo.name, 'foo'); diff --git a/test/function/samples/class-name-conflict/declaration2.js b/test/function/samples/class-name-conflict/declaration2.js new file mode 100644 index 00000000000..ea4c231065c --- /dev/null +++ b/test/function/samples/class-name-conflict/declaration2.js @@ -0,0 +1,3 @@ +class foo {} + +assert.strictEqual(foo.name, 'foo'); diff --git a/test/function/samples/class-name-conflict/expression1.js b/test/function/samples/class-name-conflict/expression1.js new file mode 100644 index 00000000000..f081a730090 --- /dev/null +++ b/test/function/samples/class-name-conflict/expression1.js @@ -0,0 +1,3 @@ +let foo = class {}; + +assert.strictEqual(foo.name, 'foo'); diff --git a/test/function/samples/class-name-conflict/expression2.js b/test/function/samples/class-name-conflict/expression2.js new file mode 100644 index 00000000000..f081a730090 --- /dev/null +++ b/test/function/samples/class-name-conflict/expression2.js @@ -0,0 +1,3 @@ +let foo = class {}; + +assert.strictEqual(foo.name, 'foo'); diff --git a/test/function/samples/class-name-conflict/main.js b/test/function/samples/class-name-conflict/main.js new file mode 100644 index 00000000000..a72f0553ec3 --- /dev/null +++ b/test/function/samples/class-name-conflict/main.js @@ -0,0 +1,4 @@ +import './expression1'; +import './declaration1'; +import './expression2'; +import './declaration2';