This repository has been archived by the owner on Jul 14, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
SpringBootProjectStructure.ts
131 lines (119 loc) · 5.24 KB
/
SpringBootProjectStructure.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/*
* Copyright © 2019 Atomist, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Java9FileParser, KotlinFileParser } from "@atomist/antlr";
import {
astUtils,
FileParser,
FileParserRegistry,
logger,
Project,
ProjectFile,
} from "@atomist/automation-client";
import {
evaluateScalarValue,
PathExpression,
} from "@atomist/tree-path";
import * as path from "path";
import { KotlinPackage } from "../../java/JavaProjectStructure";
import {
JavaSourceFiles,
KotlinSourceFiles,
} from "../../java/javaProjectUtils";
import { packageInfo } from "../../java/query/packageInfo";
/**
* Path expression for a class name annotated with Spring Boot.
* Uses Java formal grammar.
* @type {string}
*/
export const SpringBootAppClassInJava = `//normalClassDeclaration
[//annotation[@value='@SpringBootApplication']]
/identifier`;
/**
* Path expression for a class name annotated with Spring Boot.
* Uses Kotlin formal grammar.
* @type {string}
*/
export const SpringBootAppClassInKotlin = `//classDeclaration
[//annotation[@value='@SpringBootApplication']]
//Identifier`;
/**
* Represents the structure of a Spring Boot project,
* which can be inferred from its contents. Covers application class
* and starters.
*/
export class SpringBootProjectStructure {
/**
* Infer the Spring project structure of the given project, if found
* @param {ProjectAsync} p
* @return {Promise<SpringBootProjectStructure>}
*/
public static async inferFromJavaSource(p: Project, globOptions: string | string[] = JavaSourceFiles): Promise<SpringBootProjectStructure> {
return this.inferFromSourceWithJavaLikeImports(p, Java9FileParser, globOptions, SpringBootAppClassInJava);
}
public static async inferFromKotlinSource(p: Project, globOptions: string | string[] = KotlinSourceFiles): Promise<SpringBootProjectStructure> {
return this.inferFromSourceWithJavaLikeImports(p, KotlinFileParser, globOptions, SpringBootAppClassInKotlin);
}
public static async inferFromJavaOrKotlinSource(p: Project): Promise<SpringBootProjectStructure> {
return await this.inferFromJavaSource(p) || this.inferFromKotlinSource(p);
}
private static async inferFromSourceWithJavaLikeImports(p: Project,
parserOrRegistry: FileParser | FileParserRegistry,
globOptions: string | string[],
pathExpression: string | PathExpression): Promise<SpringBootProjectStructure> {
const fileHits = await astUtils.findFileMatches(p, parserOrRegistry, globOptions, pathExpression);
if (fileHits.length === 0) {
return undefined;
}
if (fileHits.length > 1) {
const msg = `Found more than one Spring Boot application annotation in files: ` +
fileHits.map(f => path.join(f.file.path, f.file.name)).join(",");
logger.warn(msg);
throw new Error(msg);
}
const fh = fileHits[0];
// It's in the default package if no match found
const packageName: { name: string } = {
name: fh.file.extension === "java" ?
// TODO using package workaround for Antlr bug
((await packageInfo(p, fh.file.path)) || { fqn: "" }).fqn :
evaluateScalarValue(fh.fileNode, KotlinPackage) ||
"",
};
const appClass = fh.matches[0].$value;
if (packageName && appClass) {
logger.debug("Successful Spring Boot inference on %j: packageName '%s', '%s'",
p.id, packageName.name, appClass);
return new SpringBootProjectStructure(packageName.name, appClass, fh.file);
} else {
logger.debug("Unsuccessful Spring Boot inference on %j: packageName '%j', '%s'",
p.id, packageName, appClass);
return undefined;
}
}
/**
* The stem of the application class. Strip "Application" if present.
*/
public applicationClassStem = this.applicationClass.replace(/Application$/, "");
/**
* @param applicationPackage The package with the Spring Boot application class in it.
* @param applicationClass Name of the application class within the given package
* @param appClassFile path to the file containing the @SpringBootApplication annotation
*/
private constructor(public readonly applicationPackage: string,
public readonly applicationClass: string,
public readonly appClassFile: ProjectFile) {
}
}