Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed May 26, 2024
1 parent ba15d34 commit de36aa6
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 3 deletions.
85 changes: 82 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,78 @@ You can use `node run.mjs` after `npm install` to run and update the tests below

### esbuild (`esbuild@0.21.3`)

✅ All checks passed
Known issues:

* Decorator metadata is only applied to classes with a class-level decorator due to [this bug](https://github.com/evanw/esbuild/issues/3781).

<details>
<summary>❌ 14 checks failed (click for details)</summary>

```
❌ Decorator metadata: class statement
Code: order(foo)
Throws: TypeError: Cannot read properties of null (reading 'staticAccessor')
❌ Decorator metadata: class statement
Code: Object.getPrototypeOf(foo)
Throws: TypeError: Cannot convert undefined or null to object
❌ Decorator metadata: class statement
Code: order(bar)
Throws: TypeError: Cannot read properties of null (reading 'staticAccessor')
❌ Decorator metadata: class statement
Code: Object.getPrototypeOf(bar)
Throws: TypeError: Cannot convert undefined or null to object
❌ Decorator metadata: class statement
Code: JSON.stringify(FooOneDec[Symbol.metadata])
Expected: "{\"x\":22}"
Observed: "null"
❌ Decorator metadata: class statement
Code: JSON.stringify(BarOneDec[Symbol.metadata])
Expected: "{\"y\":23}"
Observed: "null"
❌ Decorator metadata: class statement
Code: Object.getPrototypeOf(BarOneDec[Symbol.metadata])
Throws: TypeError: Cannot convert undefined or null to object
❌ Decorator metadata: class expression
Code: order(foo)
Throws: TypeError: Cannot read properties of null (reading 'staticAccessor')
❌ Decorator metadata: class expression
Code: Object.getPrototypeOf(foo)
Throws: TypeError: Cannot convert undefined or null to object
❌ Decorator metadata: class expression
Code: order(bar)
Throws: TypeError: Cannot read properties of null (reading 'staticAccessor')
❌ Decorator metadata: class expression
Code: Object.getPrototypeOf(bar)
Throws: TypeError: Cannot convert undefined or null to object
❌ Decorator metadata: class expression
Code: JSON.stringify(FooOneDec[Symbol.metadata])
Expected: "{\"x\":22}"
Observed: "null"
❌ Decorator metadata: class expression
Code: JSON.stringify(BarOneDec[Symbol.metadata])
Expected: "{\"y\":23}"
Observed: "null"
❌ Decorator metadata: class expression
Code: Object.getPrototypeOf(BarOneDec[Symbol.metadata])
Throws: TypeError: Cannot convert undefined or null to object
❌ 14 checks failed
```

</details>

### Babel (`@babel/plugin-proposal-decorators@7.24.6`)

Expand Down Expand Up @@ -327,7 +398,7 @@ Known issues:
* Generated code sometimes has syntax errors caused by duplicate private names in the same class.

<details>
<summary>❌ 238 checks failed (click for details)</summary>
<summary>❌ 240 checks failed (click for details)</summary>

```
❌ Class decorators: Basic expression: Property value
Expand Down Expand Up @@ -1359,6 +1430,10 @@ Known issues:
Expected: "0,1,2,3,11,12,13,14,4,5,6,7,15,16,17,18,8,19,9,20,10,21"
Observed: "0,1,2,3,11,12,13,14,5,6,7,8,16,17,18,19,4,15,9,20,10,21"
❌ Decorator metadata: class statement
Code: ()=>Object.getPrototypeOf(BarOneDec[Symbol.metadata])
Throws: TypeError: Cannot convert object to primitive value
❌ Decorator metadata: class expression
Code: ()=>order(foo)
Expected: "0,1,2,3,,,,,4,5,6,7,,,,,8,,9,,10,"
Expand All @@ -1369,6 +1444,10 @@ Known issues:
Expected: "0,1,2,3,11,12,13,14,4,5,6,7,15,16,17,18,8,19,9,20,10,21"
Observed: "0,1,2,3,11,12,13,14,5,6,7,8,16,17,18,19,4,15,9,20,,"
❌ Decorator metadata: class expression
Code: ()=>Object.getPrototypeOf(BarOneDec[Symbol.metadata])
Throws: TypeError: Cannot convert object to primitive value
❌ Initializer order (public members, class statement)
Code: ()=>log + ''
Expected: "start,extends,M1,M2,G1,G2,S1,S2,A1,A2,m1,m2,g1,g2,s1,s2,a1,a2,F1,F2,f1,f2,c1,c2,M3,M4,M5,M6,G3,G4,G5,G6,S3,S4,S5,S6,static:start,F7,F8,F3,F4,F5,F6,A7,A8,A3,A4,A5,A6,static:end,c3,c4,c5,c6,after,ctor:start,m3,m4,m5,m6,g3,g4,g5,g6,s3,s4,s5,s6,f7,f8,f3,f4,f5,f6,a7,a8,a3,a4,a5,a6,ctor:end,end"
Expand All @@ -1387,7 +1466,7 @@ Known issues:
Expected: "start,extends,M1,M2,G1,G2,S1,S2,A1,A2,m1,m2,g1,g2,s1,s2,a1,a2,F1,F2,f1,f2,c1,c2,M3,M4,M5,M6,G3,G4,G5,G6,S3,S4,S5,S6,static:start,F7,F8,F3,F4,F5,F6,A7,A8,A3,A4,A5,A6,static:end,c3,c4,c5,c6,after,ctor:start,m3,m4,m5,m6,g3,g4,g5,g6,s3,s4,s5,s6,f7,f8,f3,f4,f5,f6,a7,a8,a3,a4,a5,a6,ctor:end,end"
Observed: "start,extends,M1,M2,G1,G2,S1,S2,A1,A2,F1,F2,m1,m2,g1,g2,s1,s2,a1,a2,f1,f2,c1,c2,M3,M4,M5,M6,G3,G4,G5,G6,S3,S4,S5,S6,A3,A4,A5,A6,F3,F4,F5,F6,static:start,F8,F7,A8,A7,static:end,c3,c4,c5,c6,after,ctor:start,f8,f7,m3,m4,m5,m6,g3,g4,g5,g6,s3,s4,s5,s6,a3,a4,a5,a6,f3,f4,f5,f6,a8,a7,ctor:end,end"
238 checks failed
240 checks failed
```

</details>
41 changes: 41 additions & 0 deletions decorator-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
if (!('metadata' in Symbol)) {
Symbol.metadata = Symbol('Symbol.metadata');
}
if (!(Symbol.metadata in Function)) {
Object.defineProperty(Function.prototype, Symbol.metadata, { value: null });
}
const tests = {
// Class decorators
'Class decorators: Basic statement': () => {
Expand Down Expand Up @@ -3264,6 +3267,25 @@ const tests = {
assertEq(() => Object.getPrototypeOf(foo), null);
assertEq(() => order(bar), '0,1,2,3,11,12,13,14,4,5,6,7,15,16,17,18,8,19,9,20,10,21');
assertEq(() => Object.getPrototypeOf(bar), foo);
// Test an undecorated class
class FooNoDec {
}
class BarNoDec extends FooNoDec {
}
assertEq(() => FooNoDec[Symbol.metadata], null);
assertEq(() => BarNoDec[Symbol.metadata], null);
// Test a class with no class decorator
class FooOneDec {
@dec
x;
}
class BarOneDec extends FooOneDec {
@dec
y;
}
assertEq(() => JSON.stringify(FooOneDec[Symbol.metadata]), JSON.stringify({ x: 22 }));
assertEq(() => JSON.stringify(BarOneDec[Symbol.metadata]), JSON.stringify({ y: 23 }));
assertEq(() => Object.getPrototypeOf(BarOneDec[Symbol.metadata]), FooOneDec[Symbol.metadata]);
},
'Decorator metadata: class expression': () => {
let counter = 0;
Expand Down Expand Up @@ -3332,6 +3354,25 @@ const tests = {
assertEq(() => Object.getPrototypeOf(foo), null);
assertEq(() => order(bar), '0,1,2,3,11,12,13,14,4,5,6,7,15,16,17,18,8,19,9,20,10,21');
assertEq(() => Object.getPrototypeOf(bar), foo);
// Test an undecorated class
const FooNoDec = class {
};
const BarNoDec = class extends FooNoDec {
};
assertEq(() => FooNoDec[Symbol.metadata], null);
assertEq(() => BarNoDec[Symbol.metadata], null);
// Test a class with no class decorator
const FooOneDec = class {
@dec
x;
};
const BarOneDec = class extends FooOneDec {
@dec
y;
};
assertEq(() => JSON.stringify(FooOneDec[Symbol.metadata]), JSON.stringify({ x: 22 }));
assertEq(() => JSON.stringify(BarOneDec[Symbol.metadata]), JSON.stringify({ y: 23 }));
assertEq(() => Object.getPrototypeOf(BarOneDec[Symbol.metadata]), FooOneDec[Symbol.metadata]);
},
// Initializer order
'Initializer order (public members, class statement)': () => {
Expand Down
29 changes: 29 additions & 0 deletions decorator-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
if (!('metadata' in Symbol as any)) {
(Symbol as any).metadata = Symbol('Symbol.metadata')
}
if (!(Symbol.metadata in Function)) {
Object.defineProperty((Function as any).prototype, Symbol.metadata, { value: null })
}

const tests: Record<string, () => Promise<void> | void> = {
// Class decorators
Expand Down Expand Up @@ -2850,6 +2853,19 @@ const tests: Record<string, () => Promise<void> | void> = {
assertEq(() => Object.getPrototypeOf(foo), null)
assertEq(() => order(bar), '0,1,2,3,11,12,13,14,4,5,6,7,15,16,17,18,8,19,9,20,10,21')
assertEq(() => Object.getPrototypeOf(bar), foo)

// Test an undecorated class
class FooNoDec { }
class BarNoDec extends FooNoDec { }
assertEq(() => FooNoDec[Symbol.metadata], null)
assertEq(() => BarNoDec[Symbol.metadata], null)

// Test a class with no class decorator
class FooOneDec { @dec x: undefined }
class BarOneDec extends FooOneDec { @dec y: undefined }
assertEq(() => JSON.stringify(FooOneDec[Symbol.metadata]!), JSON.stringify({ x: 22 }))
assertEq(() => JSON.stringify(BarOneDec[Symbol.metadata]!), JSON.stringify({ y: 23 }))
assertEq(() => Object.getPrototypeOf(BarOneDec[Symbol.metadata]!), FooOneDec[Symbol.metadata]!)
},
'Decorator metadata: class expression': () => {
let counter = 0
Expand Down Expand Up @@ -2896,6 +2912,19 @@ const tests: Record<string, () => Promise<void> | void> = {
assertEq(() => Object.getPrototypeOf(foo), null)
assertEq(() => order(bar), '0,1,2,3,11,12,13,14,4,5,6,7,15,16,17,18,8,19,9,20,10,21')
assertEq(() => Object.getPrototypeOf(bar), foo)

// Test an undecorated class
const FooNoDec = class { }
const BarNoDec = class extends FooNoDec { }
assertEq(() => FooNoDec[Symbol.metadata], null)
assertEq(() => BarNoDec[Symbol.metadata], null)

// Test a class with no class decorator
const FooOneDec = class { @dec x: undefined }
const BarOneDec = class extends FooOneDec { @dec y: undefined }
assertEq(() => JSON.stringify(FooOneDec[Symbol.metadata]!), JSON.stringify({ x: 22 }))
assertEq(() => JSON.stringify(BarOneDec[Symbol.metadata]!), JSON.stringify({ y: 23 }))
assertEq(() => Object.getPrototypeOf(BarOneDec[Symbol.metadata]!), FooOneDec[Symbol.metadata]!)
},

// Initializer order
Expand Down
1 change: 1 addition & 0 deletions run.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ fs.writeFileSync('./decorator-tests.js', `// Note: Edit "decorator-tests.ts" ins
// Check esbuild
await checkBehavior('esbuild', `esbuild@${require('esbuild/package.json').version}`,
() => esbuild.transformSync(js, { target: 'es2022' }).code, [
'* Decorator metadata is only applied to classes with a class-level decorator due to [this bug](https://github.com/evanw/esbuild/issues/3781).',
])

// Check Babel
Expand Down

0 comments on commit de36aa6

Please sign in to comment.