@@ -13,45 +13,91 @@ import { normalizeAssetsDir } from '../../component-plugins/assets-plugin';
1313import { normalizeStyles } from '../../style/normalize-styles' ;
1414import { validateComponentClass } from './validate-component' ;
1515import * as ts from 'typescript' ;
16+ import { buildError } from '../../util' ;
17+ import { isDecoratorNamed } from './utils' ;
1618
1719
1820export function gatherMetadata ( config : d . Config , compilerCtx : d . CompilerCtx , buildCtx : d . BuildCtx , typeChecker : ts . TypeChecker ) : ts . TransformerFactory < ts . SourceFile > {
1921
2022 return ( transformContext ) => {
2123
22- function visit ( node : ts . Node , tsSourceFile : ts . SourceFile , moduleFile : d . ModuleFile ) : ts . VisitResult < ts . Node > {
24+ function visit ( node : ts . Node , tsSourceFile : ts . SourceFile , moduleFile : d . ModuleFile ) {
2325
24- if ( node . kind === ts . SyntaxKind . ImportDeclaration ) {
25- getCollections ( config , compilerCtx , buildCtx . collections , moduleFile , node as ts . ImportDeclaration ) ;
26- }
27-
28- if ( ts . isClassDeclaration ( node ) ) {
29- const cmpMeta = visitClass ( buildCtx . diagnostics , typeChecker , node as ts . ClassDeclaration , tsSourceFile ) ;
30- if ( cmpMeta ) {
31- moduleFile . cmpMeta = cmpMeta ;
26+ try {
27+ if ( node . kind === ts . SyntaxKind . ImportDeclaration ) {
28+ getCollections ( config , compilerCtx , buildCtx . collections , moduleFile , node as ts . ImportDeclaration ) ;
29+ }
3230
33- cmpMeta . stylesMeta = normalizeStyles ( config , moduleFile . sourceFilePath , cmpMeta . stylesMeta ) ;
34- cmpMeta . assetsDirsMeta = normalizeAssetsDir ( config , moduleFile . sourceFilePath , cmpMeta . assetsDirsMeta ) ;
31+ if ( ts . isClassDeclaration ( node ) ) {
32+ const cmpMeta = visitClass ( buildCtx . diagnostics , typeChecker , node as ts . ClassDeclaration , tsSourceFile ) ;
33+ if ( cmpMeta ) {
34+ if ( moduleFile . cmpMeta ) {
35+ throw new Error ( `More than one @Component() class in a single file is not valid` ) ;
36+ }
37+ moduleFile . cmpMeta = cmpMeta ;
38+
39+ cmpMeta . stylesMeta = normalizeStyles ( config , moduleFile . sourceFilePath , cmpMeta . stylesMeta ) ;
40+ cmpMeta . assetsDirsMeta = normalizeAssetsDir ( config , moduleFile . sourceFilePath , cmpMeta . assetsDirsMeta ) ;
41+ }
3542 }
36- }
43+ return node ;
3744
38- return ts . visitEachChild ( node , ( node ) => {
39- return visit ( node , tsSourceFile , moduleFile ) ;
40- } , transformContext ) ;
45+ } catch ( { message} ) {
46+ const error = buildError ( buildCtx . diagnostics ) ;
47+ error . messageText = message ;
48+ error . relFilePath = tsSourceFile . fileName ;
49+ }
50+ return undefined ;
4151 }
4252
4353 return ( tsSourceFile ) => {
4454 const moduleFile = getModuleFile ( compilerCtx , tsSourceFile . fileName ) ;
4555 moduleFile . externalImports . length = 0 ;
4656 moduleFile . localImports . length = 0 ;
57+ moduleFile . cmpMeta = undefined ;
4758
48- return visit ( tsSourceFile , tsSourceFile , moduleFile ) as ts . SourceFile ;
59+ const results = ts . visitEachChild ( tsSourceFile , ( node ) => {
60+ return visit ( node , tsSourceFile , moduleFile ) ;
61+ } , transformContext ) ;
62+
63+ if ( moduleFile . cmpMeta ) {
64+ const fileSymbol = typeChecker . getSymbolAtLocation ( tsSourceFile ) ;
65+ const fileExports = ( fileSymbol && typeChecker . getExportsOfModule ( fileSymbol ) ) || [ ] ;
66+
67+ if ( fileExports . length > 1 ) {
68+ const error = buildError ( buildCtx . diagnostics ) ;
69+ error . messageText = `@Component() must be the only export of the module` ;
70+ error . relFilePath = tsSourceFile . fileName ;
71+
72+ } else if (
73+ fileExports . length === 0 ||
74+ ! isComponentClass ( fileExports [ 0 ] )
75+ ) {
76+ const error = buildError ( buildCtx . diagnostics ) ;
77+ error . messageText = `Missing export in @Component() class` ;
78+ error . relFilePath = tsSourceFile . fileName ;
79+ }
80+ }
81+ return results ;
4982 } ;
5083 } ;
5184}
5285
86+ function isComponentClass ( symbol : ts . Symbol ) {
87+ const decorators = symbol . valueDeclaration && symbol . valueDeclaration . decorators ;
88+ if ( ! decorators ) {
89+ return false ;
90+ }
91+ return isDecoratorNamed ( 'Component' ) ( decorators [ 0 ] ) ;
92+ }
93+
5394
54- export function visitClass ( diagnostics : d . Diagnostic [ ] , typeChecker : ts . TypeChecker , classNode : ts . ClassDeclaration , sourceFile : ts . SourceFile ) : d . ComponentMeta | undefined {
95+ export function visitClass (
96+ diagnostics : d . Diagnostic [ ] ,
97+ typeChecker : ts . TypeChecker ,
98+ classNode : ts . ClassDeclaration ,
99+ sourceFile : ts . SourceFile ,
100+ ) : d . ComponentMeta | undefined {
55101 const cmpMeta = getComponentDecoratorMeta ( diagnostics , typeChecker , classNode ) ;
56102
57103 if ( ! cmpMeta ) {
0 commit comments