11import type { Plugin } from 'vite' ;
2- import ts from 'typescript' ;
2+ import type * as TS from 'typescript' ;
33import { isNativeESClassesEnabled , transformNativeClassSource } from './nativeclass-transform.js' ;
44import { resolvePlatform } from './cli-flags.js' ;
5+ import { loadTypeScript , warnNativeClassSkipped , type TypeScript } from './typescript.js' ;
56
67/**
78 * Look for `NativeClass` either as a bare identifier or as a `NativeClass(...)` call expression
89 * inside a `__decorate` array element. Returns true if the element is a NativeClass marker.
910 */
10- function isNativeClassDecoratorElement ( el : ts . Expression ) : boolean {
11+ function isNativeClassDecoratorElement ( ts : TypeScript , el : TS . Expression ) : boolean {
1112 if ( ts . isIdentifier ( el ) && el . text === 'NativeClass' ) return true ;
1213 if ( ts . isCallExpression ( el ) && ts . isIdentifier ( el . expression ) && el . expression . text === 'NativeClass' ) return true ;
1314 return false ;
@@ -28,19 +29,19 @@ function isNativeClassDecoratorElement(el: ts.Expression): boolean {
2829 * decorators in the array (e.g. `__metadata("design:paramtypes", [])`), which
2930 * Angular's compiler always emits when a class has a constructor.
3031 */
31- function collectNativeClassDecorateEdits ( code : string , sf : ts . SourceFile ) : { edits : Array < { start : number ; end : number ; text : string } > ; classNames : Set < string > } {
32+ function collectNativeClassDecorateEdits ( ts : TypeScript , code : string , sf : TS . SourceFile ) : { edits : Array < { start : number ; end : number ; text : string } > ; classNames : Set < string > } {
3233 const edits : Array < { start : number ; end : number ; text : string } > = [ ] ;
3334 const classNames = new Set < string > ( ) ;
3435
35- const visit = ( node : ts . Node ) : void => {
36+ const visit = ( node : TS . Node ) : void => {
3637 if ( ts . isCallExpression ( node ) ) {
3738 const callee = node . expression ;
3839 const calleeName = ts . isIdentifier ( callee ) ? callee . text : ts . isPropertyAccessExpression ( callee ) && ts . isIdentifier ( callee . expression ) && ts . isIdentifier ( callee . name ) ? `${ callee . expression . text } .${ callee . name . text } ` : undefined ;
3940 if ( calleeName && / ^ _ _ d e c o r a t e / . test ( calleeName ) && node . arguments . length >= 2 ) {
4041 const firstArg = node . arguments [ 0 ] ;
4142 const secondArg = node . arguments [ 1 ] ;
4243 if ( ts . isArrayLiteralExpression ( firstArg ) && ts . isIdentifier ( secondArg ) ) {
43- const remaining = firstArg . elements . filter ( ( el ) => ! isNativeClassDecoratorElement ( el ) ) ;
44+ const remaining = firstArg . elements . filter ( ( el ) => ! isNativeClassDecoratorElement ( ts , el ) ) ;
4445 if ( remaining . length !== firstArg . elements . length ) {
4546 classNames . add ( secondArg . text ) ;
4647 const callStart = node . getStart ( sf ) ;
@@ -79,10 +80,10 @@ function collectNativeClassDecorateEdits(code: string, sf: ts.SourceFile): { edi
7980 * runtime global, so any pre-existing tslib `__extends` named import is removed
8081 * from this file's `tslib` import.
8182 */
82- function stripExtendsImportFromTslib ( code : string ) : string {
83+ function stripExtendsImportFromTslib ( ts : TypeScript , code : string ) : string {
8384 if ( ! / \b _ _ e x t e n d s \b / . test ( code ) ) return code ;
8485
85- let sf : ts . SourceFile ;
86+ let sf : TS . SourceFile ;
8687 try {
8788 sf = ts . createSourceFile ( 'extends-import-check.js' , code , ts . ScriptTarget . Latest , true , ts . ScriptKind . JS ) ;
8889 } catch {
@@ -136,16 +137,21 @@ function stripExtendsImportFromTslib(code: string): string {
136137 */
137138export function postCleanupNativeClass ( code : string , bareId : string , verbose = false ) : { code : string ; map : null } | null {
138139 if ( ! code ) return null ;
139- if ( ! code . includes ( '__decorate' ) || ! code . includes ( 'NativeClass' ) ) return null ;
140+ if ( ! code . includes ( '__decorate' ) || ! / \b N a t i v e C l a s s \b / . test ( code ) ) return null ;
141+ const ts = loadTypeScript ( ) ;
142+ if ( ! ts ) {
143+ warnNativeClassSkipped ( bareId ) ;
144+ return null ;
145+ }
140146
141- let sf : ts . SourceFile ;
147+ let sf : TS . SourceFile ;
142148 try {
143149 sf = ts . createSourceFile ( bareId + '.js' , code , ts . ScriptTarget . Latest , true , ts . ScriptKind . JS ) ;
144150 } catch {
145151 return null ;
146152 }
147153
148- const { edits : decorateEdits , classNames : classNamesToDownlevel } = collectNativeClassDecorateEdits ( code , sf ) ;
154+ const { edits : decorateEdits , classNames : classNamesToDownlevel } = collectNativeClassDecorateEdits ( ts , code , sf ) ;
149155
150156 if ( ! classNamesToDownlevel . size ) return null ;
151157
@@ -167,13 +173,13 @@ export function postCleanupNativeClass(code: string, bareId: string, verbose = f
167173 try {
168174 // Use TypeScript AST to find and extract the class expression reliably
169175 const sf = ts . createSourceFile ( bareId + '.js' , output , ts . ScriptTarget . Latest , true , ts . ScriptKind . JS ) ;
170- let classNode : ts . ClassExpression | ts . ClassDeclaration | undefined ;
176+ let classNode : TS . ClassExpression | TS . ClassDeclaration | undefined ;
171177 let baseName = '' ;
172178 let varDeclStart = - 1 ;
173179 let varDeclEnd = - 1 ;
174180 let aliasName = '' ; // e.g. PDFViewDelegateImpl_1
175181
176- const findClass = ( node : ts . Node ) => {
182+ const findClass = ( node : TS . Node ) => {
177183 if ( classNode ) return ;
178184 // Match: var X = class X extends Y { ... }
179185 // or: var X = X_1 = class X extends Y { ... }
@@ -261,7 +267,7 @@ export function postCleanupNativeClass(code: string, bareId: string, verbose = f
261267 // `_super.call(this)` runs. Let the bare `__extends(...)` reference fall
262268 // through to the runtime global.
263269 if ( downleveledAtLeastOne ) {
264- output = stripExtendsImportFromTslib ( output ) ;
270+ output = stripExtendsImportFromTslib ( ts , output ) ;
265271 }
266272
267273 if ( output !== code ) {
0 commit comments