Skip to content

Commit 669a8ca

Browse files
Fix(turbopack): Resolve experimentalDecorators from extended tsconfig (#79755)
Co-authored-by: Jude Gao <[email protected]>
1 parent 3a0af14 commit 669a8ca

File tree

3 files changed

+127
-71
lines changed

3 files changed

+127
-71
lines changed

crates/next-core/src/transform_options.rs

Lines changed: 53 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -66,51 +66,62 @@ pub async fn get_decorators_transform_options(
6666
) -> Result<Vc<DecoratorsOptions>> {
6767
let tsconfig = get_typescript_options(project_path).await?;
6868

69-
let decorators_transform_options = if let Some(tsconfig) = tsconfig {
70-
read_from_tsconfigs(&tsconfig, |json, _| {
71-
let decorators_kind = if json["compilerOptions"]["experimentalDecorators"]
72-
.as_bool()
73-
.unwrap_or(false)
74-
{
75-
Some(DecoratorsKind::Legacy)
76-
} else {
77-
// ref: https://devblogs.microsoft.com/typescript/announcing-typescript-5-0-rc/#differences-with-experimental-legacy-decorators
78-
// `without the flag, decorators will now be valid syntax for all new code.
79-
// Outside of --experimentalDecorators, they will be type-checked and emitted
80-
// differently with ts 5.0, new ecma decorators will be enabled
81-
// if legacy decorators are not enabled
82-
Some(DecoratorsKind::Ecma)
83-
};
84-
85-
let emit_decorators_metadata = if let Some(decorators_kind) = &decorators_kind {
86-
match decorators_kind {
87-
DecoratorsKind::Legacy => {
88-
// ref: This new decorators proposal is not compatible with
89-
// --emitDecoratorMetadata, and it does not allow decorating parameters.
90-
// Future ECMAScript proposals may be able to help bridge that gap
91-
json["compilerOptions"]["emitDecoratorMetadata"]
92-
.as_bool()
93-
.unwrap_or(false)
94-
}
95-
DecoratorsKind::Ecma => false,
96-
}
97-
} else {
98-
false
99-
};
100-
101-
Some(DecoratorsOptions {
102-
decorators_kind,
103-
emit_decorators_metadata,
104-
use_define_for_class_fields: json["compilerOptions"]["useDefineForClassFields"]
105-
.as_bool()
106-
.unwrap_or(false),
107-
..Default::default()
108-
})
69+
let experimental_decorators = if let Some(ref tsconfig) = tsconfig {
70+
read_from_tsconfigs(tsconfig, |json, _| {
71+
json["compilerOptions"]["experimentalDecorators"].as_bool()
10972
})
11073
.await?
111-
.unwrap_or_default()
74+
.unwrap_or(false)
75+
} else {
76+
false
77+
};
78+
79+
let decorators_kind = if experimental_decorators {
80+
Some(DecoratorsKind::Legacy)
11281
} else {
113-
Default::default()
82+
// ref: https://devblogs.microsoft.com/typescript/announcing-typescript-5-0-rc/#differences-with-experimental-legacy-decorators
83+
// `without the flag, decorators will now be valid syntax for all new code.
84+
// Outside of --experimentalDecorators, they will be type-checked and emitted
85+
// differently with ts 5.0, new ecma decorators will be enabled
86+
// if legacy decorators are not enabled
87+
Some(DecoratorsKind::Ecma)
88+
};
89+
90+
let emit_decorators_metadata = if let Some(ref tsconfig) = tsconfig {
91+
read_from_tsconfigs(tsconfig, |json, _| {
92+
json["compilerOptions"]["emitDecoratorMetadata"].as_bool()
93+
})
94+
.await?
95+
.unwrap_or(false)
96+
} else {
97+
false
98+
};
99+
100+
let use_define_for_class_fields = if let Some(ref tsconfig) = tsconfig {
101+
read_from_tsconfigs(tsconfig, |json, _| {
102+
json["compilerOptions"]["useDefineForClassFields"].as_bool()
103+
})
104+
.await?
105+
.unwrap_or(false)
106+
} else {
107+
false
108+
};
109+
110+
let decorators_transform_options = DecoratorsOptions {
111+
decorators_kind: decorators_kind.clone(),
112+
emit_decorators_metadata: if let Some(ref decorators_kind) = decorators_kind {
113+
match decorators_kind {
114+
DecoratorsKind::Legacy => emit_decorators_metadata,
115+
// ref: This new decorators proposal is not compatible with
116+
// --emitDecoratorMetadata, and it does not allow decorating parameters.
117+
// Future ECMAScript proposals may be able to help bridge that gap
118+
DecoratorsKind::Ecma => false,
119+
}
120+
} else {
121+
false
122+
},
123+
use_define_for_class_fields,
124+
..Default::default()
114125
};
115126

116127
Ok(decorators_transform_options.cell())

test/development/basic/legacy-decorators.test.ts

Lines changed: 71 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,81 @@ import { NextInstance } from 'e2e-utils'
55
import { check } from 'next-test-utils'
66

77
describe('Legacy decorators SWC option', () => {
8-
let next: NextInstance
8+
describe('with extended tsconfig', () => {
9+
let next: NextInstance
910

10-
beforeAll(async () => {
11-
next = await createNext({
12-
files: {
13-
'jsconfig.json': new FileRef(
14-
join(__dirname, 'legacy-decorators/jsconfig.json')
15-
),
16-
pages: new FileRef(join(__dirname, 'legacy-decorators/pages')),
17-
},
18-
dependencies: {
19-
mobx: '6.3.7',
20-
'mobx-react': '7.2.1',
21-
},
11+
beforeAll(async () => {
12+
next = await createNext({
13+
files: {
14+
'tsconfig.json': new FileRef(
15+
join(__dirname, 'legacy-decorators/tsconfig-extended.json')
16+
),
17+
'tsconfig-base.json': new FileRef(
18+
join(__dirname, 'legacy-decorators/jsconfig.json')
19+
),
20+
pages: new FileRef(join(__dirname, 'legacy-decorators/pages')),
21+
},
22+
dependencies: {
23+
mobx: '6.3.7',
24+
'mobx-react': '7.2.1',
25+
},
26+
})
27+
})
28+
afterAll(() => next.destroy())
29+
30+
it('should compile with legacy decorators enabled from extended config', async () => {
31+
let browser
32+
try {
33+
browser = await webdriver(next.url, '/')
34+
const text = await browser.elementByCss('#count').text()
35+
expect(text).toBe('Current number: 0')
36+
await browser.elementByCss('#increase').click()
37+
await check(
38+
() => browser.elementByCss('#count').text(),
39+
/Current number: 1/
40+
)
41+
} finally {
42+
if (browser) {
43+
await browser.close()
44+
}
45+
}
2246
})
2347
})
24-
afterAll(() => next.destroy())
2548

26-
it('should compile with legacy decorators enabled', async () => {
27-
let browser
28-
try {
29-
browser = await webdriver(next.url, '/')
30-
const text = await browser.elementByCss('#count').text()
31-
expect(text).toBe('Current number: 0')
32-
await browser.elementByCss('#increase').click()
33-
await check(
34-
() => browser.elementByCss('#count').text(),
35-
/Current number: 1/
36-
)
37-
} finally {
38-
if (browser) {
39-
await browser.close()
49+
describe('with base config', () => {
50+
let next: NextInstance
51+
beforeAll(async () => {
52+
next = await createNext({
53+
files: {
54+
'jsconfig.json': new FileRef(
55+
join(__dirname, 'legacy-decorators/jsconfig.json')
56+
),
57+
pages: new FileRef(join(__dirname, 'legacy-decorators/pages')),
58+
},
59+
dependencies: {
60+
mobx: '6.3.7',
61+
'mobx-react': '7.2.1',
62+
},
63+
})
64+
})
65+
afterAll(() => next.destroy())
66+
67+
it('should compile with legacy decorators enabled', async () => {
68+
let browser
69+
try {
70+
browser = await webdriver(next.url, '/')
71+
const text = await browser.elementByCss('#count').text()
72+
expect(text).toBe('Current number: 0')
73+
await browser.elementByCss('#increase').click()
74+
await check(
75+
() => browser.elementByCss('#count').text(),
76+
/Current number: 1/
77+
)
78+
} finally {
79+
if (browser) {
80+
await browser.close()
81+
}
4082
}
41-
}
83+
})
4284
})
4385
})
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "./tsconfig-base.json"
3+
}

0 commit comments

Comments
 (0)