Skip to content

Commit 6b66661

Browse files
committed
Theme: Accept TypeScript decorator non-null assertions
1 parent 1a50911 commit 6b66661

2 files changed

Lines changed: 34 additions & 2 deletions

File tree

‎packages/theme/js-plugins/test/build-plugins.test.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,10 @@ export const manual = 'var(--wpds-dimension-gap-sm,)';
200200
'import assertions',
201201
'import config from "./config.json" assert { type: "json" }; export const gap = config.prefix + "var(--wpds-dimension-gap-sm)";',
202202
],
203+
[
204+
'decorator non-null assertions',
205+
'function inject(...args: unknown[]) {} function factory<T>() { return inject; } @factory!<string>() class Legacy {} export @inject! class Modern {} class Styles { @inject! gap = "var(--wpds-dimension-gap-sm)"; } export const gap = new Styles().gap;',
206+
],
203207
] )( 'builds supported %s syntax', async ( _syntax, source ) => {
204208
await writeFile(
205209
join( directory, 'tsconfig.json' ),

‎packages/theme/js-plugins/transform-ds-token-fallbacks.mjs‎

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,39 @@ export function transformDsTokenFallbacks( source, filename ) {
7777
'deferredImportEvaluation',
7878
],
7979
};
80-
let ast = parse( source, options );
80+
let parserSource = source;
81+
/** @type {ReturnType<typeof parse>} */
82+
let ast;
83+
for (;;) {
84+
try {
85+
ast = parse( parserSource, options );
86+
break;
87+
} catch ( error ) {
88+
const parseError =
89+
/** @type {import('@babel/parser').ParseError} */ ( error );
90+
if (
91+
! isTypeScript ||
92+
! [ 'UnexpectedLeadingDecorator', 'UnexpectedToken' ].includes(
93+
parseError.reasonCode
94+
) ||
95+
! Number.isInteger( parseError.pos ) ||
96+
parserSource[ parseError.pos ] !== '!'
97+
) {
98+
throw error;
99+
}
100+
// Babel's decorator grammar rejects TypeScript non-null assertions.
101+
// Mask only the rejected token for parsing, preserving source offsets.
102+
// The downstream compiler still receives the original assertion.
103+
parserSource =
104+
parserSource.slice( 0, parseError.pos ) +
105+
' ' +
106+
parserSource.slice( parseError.pos + 1 );
107+
}
108+
}
81109
// With error recovery, unambiguous parsing can retain module-mode errors
82110
// even after identifying a script. Reparse it under the correct rules.
83111
if ( ast.program.sourceType === 'script' && ast.errors?.length ) {
84-
ast = parse( source, { ...options, sourceType: 'script' } );
112+
ast = parse( parserSource, { ...options, sourceType: 'script' } );
85113
}
86114
// Babel's standard decorators grammar reports parameter decorators even
87115
// in TypeScript. Leave that one check to the compiler's tsconfig settings.

0 commit comments

Comments
 (0)