idlize

Форк
0
140 строк · 5.8 Кб
1
/*
2
 * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3
 * Licensed under the Apache License, Version 2.0 (the "License");
4
 * you may not use this file except in compliance with the License.
5
 * You may obtain a copy of the License at
6
 *
7
 * http://www.apache.org/licenses/LICENSE-2.0
8
 *
9
 * Unless required by applicable law or agreed to in writing, software
10
 * distributed under the License is distributed on an "AS IS" BASIS,
11
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
 * See the License for the specific language governing permissions and
13
 * limitations under the License.
14
 */
15

16
import * as ts from 'typescript';
17
import {
18
    arrayAt,
19
    error,
20
    getDeclarationsByNode,
21
} from "./util"
22

23

24
export class ImportExport {
25
    constructor(
26
        public typechecker: ts.TypeChecker,
27
        public sourceFile: ts.SourceFile,
28
    ) {}
29

30
    isRealDeclaration(declaration: ts.Declaration): boolean {
31
        if (ts.isImportSpecifier(declaration) ||
32
            ts.isExportSpecifier(declaration) ||
33
            ts.isImportClause(declaration)) return false
34
        return true
35
    }
36

37
    followDefaultImport(identifier: ts.Identifier, importClause: ts.ImportClause): ts.Declaration | undefined {
38
        const moduleSpecifier = importClause.parent.moduleSpecifier
39
        const importSym = this.typechecker.getSymbolAtLocation(moduleSpecifier)
40
        if (importSym === undefined) {
41
            return error(`Could not obtain import symbol for ${ts.idText(identifier)}`)
42
        }
43
        const exports = this.typechecker.getExportsOfModule(importSym)
44

45
        return arrayAt(exports?.[0]?.declarations, 0)
46
    }
47

48
    followModuleSpecifier(identifier: ts.Identifier, moduleSpecifier: ts.StringLiteral): ts.Declaration | undefined {
49
        const moduleSym = this.typechecker.getSymbolAtLocation(moduleSpecifier)
50
        if (moduleSym === undefined) {
51
            // TODO: The typechecker doesn't give us the symbol in this case
52
            // So be silent for now.
53
            if (moduleSpecifier.text.startsWith("#")) {
54
                return undefined
55
            }
56
            return error(`Could not obtain module symbol for ${ts.idText(identifier)}: ${moduleSpecifier.text} `)
57
        }
58
        const exports = this.typechecker.getExportsOfModule(moduleSym)
59

60
        const found = exports.find(sym => ts.symbolName(sym) == ts.idText(identifier))
61
        return arrayAt(found?.declarations, 0)
62
    }
63

64
    followImport(identifier: ts.Identifier, declaration: ts.ImportSpecifier): ts.Declaration | undefined {
65
        if (declaration.propertyName) {
66
            const real = getDeclarationsByNode(this.typechecker, declaration.propertyName)[0]
67
            if (real && this.isRealDeclaration(real)) {
68
                return real
69
            } else {
70
                return error(`TODO: unexpected declaration: ${real}`)
71
            }
72
        }
73

74
        const moduleSpecifier = declaration.parent.parent.parent.moduleSpecifier
75
        if (!ts.isStringLiteral(moduleSpecifier)) {
76
            return error("Expected module specifier literal, got: " + ts.SyntaxKind[moduleSpecifier.kind])
77
        }
78
        return this.followModuleSpecifier(identifier, moduleSpecifier)
79
    }
80

81
    followExport(identifier: ts.Identifier, exportDeclaration: ts.ExportSpecifier): ts.Declaration | undefined {
82
        const moduleSpecifier = exportDeclaration.parent.parent.moduleSpecifier
83
        const name = exportDeclaration.propertyName ? exportDeclaration.propertyName : identifier
84

85
        if (!moduleSpecifier) {
86
            if (!exportDeclaration.propertyName) {
87
                // This is
88
                // class X{}
89
                // export { X }
90
                // In this case X's declaration is the export directive,
91
                // so need a special way to get to the class declaration.
92
                return this.typechecker.getExportSpecifierLocalTargetSymbol(exportDeclaration)?.declarations?.[0]
93
            } else {
94
                // This is an export from its own module.
95
                return this.findRealDeclaration(exportDeclaration.propertyName)
96
            }
97
        }
98
        if (!ts.isStringLiteral(moduleSpecifier)) {
99
            return error("Expected module specifier literal, got: " + ts.SyntaxKind[moduleSpecifier.kind])
100
        }
101

102
        return this.followModuleSpecifier(name, moduleSpecifier)
103
    }
104

105
    findRealDeclaration(identifier: ts.Identifier): ts.NamedDeclaration | undefined {
106
        const declarations = getDeclarationsByNode(this.typechecker, identifier)
107

108
        if (declarations.length > 1) {
109
            // TODO: shall we support overloads?
110
            // TODO: shall we allow a function and a namespace?
111
            // return error(`TODO: Multiple declarations for ${ts.idText(identifier)} not supported`)
112

113
            return undefined
114
        }
115

116
        let declaration: ts.Declaration | undefined = declarations[0]
117
        while (declaration !== undefined) {
118
            if (this.isRealDeclaration(declaration)) {
119
                return declaration
120
            } else if (ts.isImportSpecifier(declaration)) {
121
                declaration = this.followImport(identifier, declaration)
122
            } else if (ts.isExportSpecifier(declaration)) {
123
                declaration = this.followExport(identifier, declaration)
124
            } else if (ts.isImportClause(declaration)) {
125
                declaration = this.followDefaultImport(identifier, declaration)
126
            } else {
127
                console.log(`@memo plugin non-fatal: Expected a real declaration for ${ts.idText(identifier)}, but got ${declaration ? ts.SyntaxKind[declaration.kind] : undefined}`)
128
                return undefined
129
            }
130
        }
131

132
        return undefined
133
    }
134

135
    findRealMethodDeclaration(member: ts.MemberName): ts.Node | undefined {
136
        if (!ts.isIdentifier(member)) return undefined
137
        const declarations = getDeclarationsByNode(this.typechecker, member)
138
        return declarations[0]
139
    }
140
}
141

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.