fragment-ts 1.0.34 → 1.0.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API.md +248 -38
- package/DOCS.md +327 -63
- package/NewCLIGENERATECOMMANDS.txt +5 -0
- package/README.md +168 -3
- package/USAGE.md +395 -2
- package/dist/cli/commands/build.command.d.ts.map +1 -1
- package/dist/cli/commands/build.command.js +20 -8
- package/dist/cli/commands/build.command.js.map +1 -1
- package/dist/cli/commands/diagnostics.command.d.ts +1 -2
- package/dist/cli/commands/diagnostics.command.d.ts.map +1 -1
- package/dist/cli/commands/diagnostics.command.js +37 -23
- package/dist/cli/commands/diagnostics.command.js.map +1 -1
- package/dist/cli/commands/generate.command.d.ts +5 -1
- package/dist/cli/commands/generate.command.d.ts.map +1 -1
- package/dist/cli/commands/generate.command.js +171 -39
- package/dist/cli/commands/generate.command.js.map +1 -1
- package/dist/cli/commands/init.command.d.ts.map +1 -1
- package/dist/cli/commands/init.command.js +98 -28
- package/dist/cli/commands/init.command.js.map +1 -1
- package/dist/cli/commands/migrate.command.d.ts +10 -17
- package/dist/cli/commands/migrate.command.d.ts.map +1 -1
- package/dist/cli/commands/migrate.command.js +133 -170
- package/dist/cli/commands/migrate.command.js.map +1 -1
- package/dist/cli/commands/serve.command.d.ts.map +1 -1
- package/dist/cli/commands/serve.command.js +9 -4
- package/dist/cli/commands/serve.command.js.map +1 -1
- package/dist/cli/commands/test.command.d.ts.map +1 -1
- package/dist/cli/commands/test.command.js +24 -6
- package/dist/cli/commands/test.command.js.map +1 -1
- package/dist/core/scanner/component-scanner.d.ts +12 -0
- package/dist/core/scanner/component-scanner.d.ts.map +1 -1
- package/dist/core/scanner/component-scanner.js +72 -14
- package/dist/core/scanner/component-scanner.js.map +1 -1
- package/dist/shared/config.utils.d.ts +58 -0
- package/dist/shared/config.utils.d.ts.map +1 -0
- package/dist/shared/config.utils.js +137 -0
- package/dist/shared/config.utils.js.map +1 -0
- package/dist/shared/env.utils.d.ts +27 -0
- package/dist/shared/env.utils.d.ts.map +1 -0
- package/dist/shared/env.utils.js +68 -0
- package/dist/shared/env.utils.js.map +1 -0
- package/dist/shared/tsconfig.utils.d.ts +122 -0
- package/dist/shared/tsconfig.utils.d.ts.map +1 -0
- package/dist/shared/tsconfig.utils.js +305 -0
- package/dist/shared/tsconfig.utils.js.map +1 -0
- package/dist/testing/runner.d.ts +9 -1
- package/dist/testing/runner.d.ts.map +1 -1
- package/dist/testing/runner.js +50 -10
- package/dist/testing/runner.js.map +1 -1
- package/dist/typeorm/typeorm-module.d.ts +1 -0
- package/dist/typeorm/typeorm-module.d.ts.map +1 -1
- package/dist/typeorm/typeorm-module.js +193 -85
- package/dist/typeorm/typeorm-module.js.map +1 -1
- package/dist/web/application.d.ts +0 -1
- package/dist/web/application.d.ts.map +1 -1
- package/dist/web/application.js +4 -26
- package/dist/web/application.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/build.command.ts +24 -9
- package/src/cli/commands/diagnostics.command.ts +42 -30
- package/src/cli/commands/generate.command.ts +212 -52
- package/src/cli/commands/init.command.ts +100 -29
- package/src/cli/commands/migrate.command.ts +145 -198
- package/src/cli/commands/serve.command.ts +181 -170
- package/src/cli/commands/test.command.ts +25 -11
- package/src/core/scanner/component-scanner.ts +100 -18
- package/src/shared/config.utils.ts +148 -0
- package/src/shared/env.utils.ts +72 -0
- package/src/shared/tsconfig.utils.ts +360 -0
- package/src/testing/runner.ts +62 -14
- package/src/typeorm/typeorm-module.ts +209 -86
- package/src/web/application.ts +4 -33
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { EnvUtils } from "./env.utils";
|
|
4
|
+
|
|
5
|
+
// Define the fragment.json structure
|
|
6
|
+
interface FragmentDatabaseConfig {
|
|
7
|
+
type?: string;
|
|
8
|
+
host?: string;
|
|
9
|
+
port?: number;
|
|
10
|
+
username?: string;
|
|
11
|
+
password?: string;
|
|
12
|
+
database?: string;
|
|
13
|
+
synchronize?: boolean;
|
|
14
|
+
logging?: boolean;
|
|
15
|
+
entities?: string[];
|
|
16
|
+
migrations?: string[];
|
|
17
|
+
subscribers?: string[];
|
|
18
|
+
pool?: any;
|
|
19
|
+
maxQueryExecutionTime?: number;
|
|
20
|
+
[key: string]: any; // Allow additional database-specific properties
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface FragmentConfig {
|
|
24
|
+
development?: {
|
|
25
|
+
database?: FragmentDatabaseConfig;
|
|
26
|
+
};
|
|
27
|
+
production?: {
|
|
28
|
+
database?: FragmentDatabaseConfig;
|
|
29
|
+
};
|
|
30
|
+
// Legacy flat structure (for backward compatibility)
|
|
31
|
+
database?: FragmentDatabaseConfig;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class ConfigUtils {
|
|
35
|
+
private static configCache: FragmentConfig | null = null;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Load and parse fragment.json with caching
|
|
39
|
+
*/
|
|
40
|
+
private static loadConfig(): FragmentConfig {
|
|
41
|
+
if (this.configCache) {
|
|
42
|
+
return this.configCache;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const configPath = path.join(process.cwd(), "fragment.json");
|
|
46
|
+
|
|
47
|
+
if (!fs.existsSync(configPath)) {
|
|
48
|
+
this.configCache = {};
|
|
49
|
+
return this.configCache;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
54
|
+
this.configCache = JSON.parse(raw);
|
|
55
|
+
|
|
56
|
+
if (!this.configCache) {
|
|
57
|
+
this.configCache = {};
|
|
58
|
+
}
|
|
59
|
+
return this.configCache;
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error("❌ Error loading fragment.json:", error);
|
|
62
|
+
this.configCache = {};
|
|
63
|
+
return this.configCache;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Interpolate environment variables in config values
|
|
69
|
+
*/
|
|
70
|
+
static interpolateEnvVars(value: any): any {
|
|
71
|
+
if (typeof value === "string") {
|
|
72
|
+
return value.replace(
|
|
73
|
+
/\$\{([^}]+)\}/g,
|
|
74
|
+
(_, varName) => EnvUtils.getString(varName) || "",
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
if (Array.isArray(value)) {
|
|
78
|
+
return value.map((item) => this.interpolateEnvVars(item));
|
|
79
|
+
}
|
|
80
|
+
if (value !== null && typeof value === "object") {
|
|
81
|
+
const result: any = {};
|
|
82
|
+
for (const key in value) {
|
|
83
|
+
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
84
|
+
result[key] = this.interpolateEnvVars(value[key]);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get the current database configuration based on environment
|
|
94
|
+
*/
|
|
95
|
+
static getDatabaseConfig(): FragmentDatabaseConfig {
|
|
96
|
+
const config = this.loadConfig();
|
|
97
|
+
const mode = EnvUtils.getEnvironmentMode();
|
|
98
|
+
|
|
99
|
+
// Try environment-specific config first
|
|
100
|
+
if (config[mode]?.database) {
|
|
101
|
+
const interpolated = this.interpolateEnvVars(config[mode].database);
|
|
102
|
+
return interpolated;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Fallback to legacy flat structure
|
|
106
|
+
if (config.database) {
|
|
107
|
+
const interpolated = this.interpolateEnvVars(config.database);
|
|
108
|
+
return interpolated;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Return empty config if none found
|
|
112
|
+
return {};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get a specific database configuration property
|
|
117
|
+
*/
|
|
118
|
+
static getDatabaseProperty<T>(property: string, defaultValue: T): T {
|
|
119
|
+
const dbConfig = this.getDatabaseConfig();
|
|
120
|
+
return (
|
|
121
|
+
dbConfig[property] !== undefined ? dbConfig[property] : defaultValue
|
|
122
|
+
) as T;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get the full fragment configuration
|
|
127
|
+
*/
|
|
128
|
+
static getFullConfig(): FragmentConfig {
|
|
129
|
+
return this.loadConfig();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Check if database feature is enabled
|
|
134
|
+
*/
|
|
135
|
+
static isDatabaseEnabled(): boolean {
|
|
136
|
+
const config = this.loadConfig();
|
|
137
|
+
const mode = EnvUtils.getEnvironmentMode();
|
|
138
|
+
|
|
139
|
+
return !!(config[mode]?.database || config.database);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Clear the configuration cache (useful for testing)
|
|
144
|
+
*/
|
|
145
|
+
static clearCache(): void {
|
|
146
|
+
this.configCache = null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
|
|
4
|
+
export class EnvUtils {
|
|
5
|
+
/**
|
|
6
|
+
* Get a string environment variable with optional default
|
|
7
|
+
*/
|
|
8
|
+
static getString(key: string, defaultValue?: string): string {
|
|
9
|
+
const value = process.env[key];
|
|
10
|
+
return value !== undefined ? value : (defaultValue ?? "");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get a boolean environment variable (supports 'true'/'false', '1'/'0')
|
|
15
|
+
*/
|
|
16
|
+
static getBoolean(key: string, defaultValue: boolean = false): boolean {
|
|
17
|
+
const value = process.env[key];
|
|
18
|
+
if (value === undefined) return defaultValue;
|
|
19
|
+
|
|
20
|
+
return ["true", "1", "yes", "on"].includes(value.toLowerCase());
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get a number environment variable
|
|
25
|
+
*/
|
|
26
|
+
static getNumber(key: string, defaultValue: number = 0): number {
|
|
27
|
+
const value = process.env[key];
|
|
28
|
+
if (value === undefined) return defaultValue;
|
|
29
|
+
|
|
30
|
+
const num = Number(value);
|
|
31
|
+
return isNaN(num) ? defaultValue : num;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get a parsed JSON environment variable
|
|
36
|
+
*/
|
|
37
|
+
static getJson<T>(key: string, defaultValue: T): T {
|
|
38
|
+
const value = process.env[key];
|
|
39
|
+
if (value === undefined) return defaultValue;
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
return JSON.parse(value) as T;
|
|
43
|
+
} catch {
|
|
44
|
+
console.warn(`⚠️ Invalid JSON in env var ${key}, using default`);
|
|
45
|
+
return defaultValue;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Check if running in development mode
|
|
51
|
+
*/
|
|
52
|
+
static isDevelopmentMode(): boolean {
|
|
53
|
+
// Check explicit FRAGMENT_DEV_MODE
|
|
54
|
+
if (process.env.FRAGMENT_DEV_MODE !== undefined) {
|
|
55
|
+
return process.env.FRAGMENT_DEV_MODE === "true";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Check NODE_ENV
|
|
59
|
+
if (process.env.NODE_ENV) {
|
|
60
|
+
return process.env.NODE_ENV === "development";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get current environment mode ('development' or 'production')
|
|
68
|
+
*/
|
|
69
|
+
static getEnvironmentMode(): "development" | "production" {
|
|
70
|
+
return this.isDevelopmentMode() ? "development" : "production";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
// src/utils/tsconfig.utils.ts
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
|
|
5
|
+
// Define the tsconfig.json structure (partial, covering common properties)
|
|
6
|
+
interface TsConfigCompilerOptions {
|
|
7
|
+
target?: string;
|
|
8
|
+
module?: string;
|
|
9
|
+
moduleResolution?: string;
|
|
10
|
+
outDir?: string;
|
|
11
|
+
rootDir?: string;
|
|
12
|
+
baseUrl?: string;
|
|
13
|
+
paths?: Record<string, string[]>;
|
|
14
|
+
declaration?: boolean;
|
|
15
|
+
sourceMap?: boolean;
|
|
16
|
+
removeComments?: boolean;
|
|
17
|
+
emitDecoratorMetadata?: boolean;
|
|
18
|
+
experimentalDecorators?: boolean;
|
|
19
|
+
allowSyntheticDefaultImports?: boolean;
|
|
20
|
+
esModuleInterop?: boolean;
|
|
21
|
+
strict?: boolean;
|
|
22
|
+
noImplicitAny?: boolean;
|
|
23
|
+
strictNullChecks?: boolean;
|
|
24
|
+
strictFunctionTypes?: boolean;
|
|
25
|
+
strictBindCallApply?: boolean;
|
|
26
|
+
strictPropertyInitialization?: boolean;
|
|
27
|
+
noImplicitThis?: boolean;
|
|
28
|
+
alwaysStrict?: boolean;
|
|
29
|
+
noUnusedLocals?: boolean;
|
|
30
|
+
noUnusedParameters?: boolean;
|
|
31
|
+
noImplicitReturns?: boolean;
|
|
32
|
+
noFallthroughCasesInSwitch?: boolean;
|
|
33
|
+
skipLibCheck?: boolean;
|
|
34
|
+
forceConsistentCasingInFileNames?: boolean;
|
|
35
|
+
[key: string]: any; // Allow additional compiler options
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface TsConfig {
|
|
39
|
+
compilerOptions?: TsConfigCompilerOptions;
|
|
40
|
+
include?: string[];
|
|
41
|
+
exclude?: string[];
|
|
42
|
+
files?: string[];
|
|
43
|
+
references?: Array<{ path: string }>;
|
|
44
|
+
extends?: string;
|
|
45
|
+
[key: string]: any; // Allow additional top-level properties
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export class TsConfigUtils {
|
|
49
|
+
private static configCache: TsConfig | null = null;
|
|
50
|
+
private static readonly DEFAULT_TSCONFIG_PATHS = [
|
|
51
|
+
"tsconfig.json",
|
|
52
|
+
"tsconfig.build.json",
|
|
53
|
+
"tsconfig.app.json",
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Load and parse tsconfig.json with caching
|
|
58
|
+
* @param configPath Optional custom path to tsconfig file
|
|
59
|
+
*/
|
|
60
|
+
private static loadConfig(configPath?: string): TsConfig {
|
|
61
|
+
if (this.configCache) {
|
|
62
|
+
return this.configCache;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let finalConfigPath: string | null = null;
|
|
66
|
+
|
|
67
|
+
if (configPath) {
|
|
68
|
+
// Use provided path
|
|
69
|
+
finalConfigPath = path.isAbsolute(configPath)
|
|
70
|
+
? configPath
|
|
71
|
+
: path.join(process.cwd(), configPath);
|
|
72
|
+
} else {
|
|
73
|
+
// Try default paths
|
|
74
|
+
for (const defaultPath of this.DEFAULT_TSCONFIG_PATHS) {
|
|
75
|
+
const fullPath = path.join(process.cwd(), defaultPath);
|
|
76
|
+
if (fs.existsSync(fullPath)) {
|
|
77
|
+
finalConfigPath = fullPath;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!finalConfigPath || !fs.existsSync(finalConfigPath)) {
|
|
84
|
+
this.configCache = {};
|
|
85
|
+
return this.configCache;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const raw = fs.readFileSync(finalConfigPath, "utf-8");
|
|
90
|
+
const parsed = JSON.parse(raw);
|
|
91
|
+
|
|
92
|
+
// Handle "extends" by merging parent config
|
|
93
|
+
if (parsed.extends) {
|
|
94
|
+
const parentConfig = this.loadParentConfig(
|
|
95
|
+
parsed.extends,
|
|
96
|
+
path.dirname(finalConfigPath),
|
|
97
|
+
);
|
|
98
|
+
this.configCache = this.mergeConfigs(parentConfig, parsed);
|
|
99
|
+
} else {
|
|
100
|
+
this.configCache = parsed;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!this.configCache) {
|
|
104
|
+
this.configCache = {};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return this.configCache;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error("❌ Error loading tsconfig.json:", error);
|
|
110
|
+
this.configCache = {};
|
|
111
|
+
return this.configCache;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Load parent config when "extends" is used
|
|
117
|
+
*/
|
|
118
|
+
private static loadParentConfig(
|
|
119
|
+
extendsPath: string,
|
|
120
|
+
basePath: string,
|
|
121
|
+
): TsConfig {
|
|
122
|
+
// Handle relative paths and node_modules
|
|
123
|
+
let parentPath: string;
|
|
124
|
+
|
|
125
|
+
if (extendsPath.startsWith(".")) {
|
|
126
|
+
// Relative path
|
|
127
|
+
parentPath = path.resolve(basePath, extendsPath);
|
|
128
|
+
if (!parentPath.endsWith(".json")) {
|
|
129
|
+
parentPath += ".json";
|
|
130
|
+
}
|
|
131
|
+
} else if (extendsPath.startsWith("/")) {
|
|
132
|
+
// Absolute path
|
|
133
|
+
parentPath = extendsPath;
|
|
134
|
+
if (!parentPath.endsWith(".json")) {
|
|
135
|
+
parentPath += ".json";
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
// Assume node_modules package
|
|
139
|
+
try {
|
|
140
|
+
// Try to resolve as a package
|
|
141
|
+
const packageJsonPath = require.resolve(`${extendsPath}/package.json`, {
|
|
142
|
+
paths: [basePath],
|
|
143
|
+
});
|
|
144
|
+
const packageDir = path.dirname(packageJsonPath);
|
|
145
|
+
parentPath = path.join(packageDir, "tsconfig.json");
|
|
146
|
+
} catch {
|
|
147
|
+
// Fallback to direct path
|
|
148
|
+
parentPath = path.join(basePath, "node_modules", extendsPath);
|
|
149
|
+
if (!parentPath.endsWith(".json")) {
|
|
150
|
+
parentPath += ".json";
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (fs.existsSync(parentPath)) {
|
|
156
|
+
try {
|
|
157
|
+
const raw = fs.readFileSync(parentPath, "utf-8");
|
|
158
|
+
const parsed = JSON.parse(raw);
|
|
159
|
+
|
|
160
|
+
// Recursively handle extends in parent
|
|
161
|
+
if (parsed.extends) {
|
|
162
|
+
const grandParent = this.loadParentConfig(
|
|
163
|
+
parsed.extends,
|
|
164
|
+
path.dirname(parentPath),
|
|
165
|
+
);
|
|
166
|
+
return this.mergeConfigs(grandParent, parsed);
|
|
167
|
+
}
|
|
168
|
+
return parsed;
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.warn(`⚠️ Could not load parent tsconfig: ${extendsPath}`);
|
|
171
|
+
return {};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return {};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Merge two tsconfig objects (child overrides parent)
|
|
180
|
+
*/
|
|
181
|
+
private static mergeConfigs(parent: TsConfig, child: TsConfig): TsConfig {
|
|
182
|
+
const result: TsConfig = { ...parent };
|
|
183
|
+
|
|
184
|
+
// Merge compilerOptions
|
|
185
|
+
if (parent.compilerOptions || child.compilerOptions) {
|
|
186
|
+
result.compilerOptions = {
|
|
187
|
+
...parent.compilerOptions,
|
|
188
|
+
...child.compilerOptions,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Override other top-level properties
|
|
193
|
+
for (const key in child) {
|
|
194
|
+
if (key !== "compilerOptions") {
|
|
195
|
+
result[key] = child[key];
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Get the full tsconfig configuration
|
|
204
|
+
* @param configPath Optional custom path to tsconfig file
|
|
205
|
+
*/
|
|
206
|
+
static getFullConfig(configPath?: string): TsConfig {
|
|
207
|
+
return this.loadConfig(configPath);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Get compiler options from tsconfig
|
|
212
|
+
* @param configPath Optional custom path to tsconfig file
|
|
213
|
+
*/
|
|
214
|
+
static getCompilerOptions(configPath?: string): TsConfigCompilerOptions {
|
|
215
|
+
const config = this.loadConfig(configPath);
|
|
216
|
+
return config.compilerOptions || {};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Get a specific compiler option value
|
|
221
|
+
* @param option The compiler option name
|
|
222
|
+
* @param defaultValue Default value if option is not set
|
|
223
|
+
* @param configPath Optional custom path to tsconfig file
|
|
224
|
+
*/
|
|
225
|
+
static getCompilerOption<T>(
|
|
226
|
+
option: string,
|
|
227
|
+
defaultValue: T,
|
|
228
|
+
configPath?: string,
|
|
229
|
+
): T {
|
|
230
|
+
const compilerOptions = this.getCompilerOptions(configPath);
|
|
231
|
+
return (
|
|
232
|
+
compilerOptions[option] !== undefined
|
|
233
|
+
? compilerOptions[option]
|
|
234
|
+
: defaultValue
|
|
235
|
+
) as T;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Check if a specific compiler option is enabled
|
|
240
|
+
* @param option The compiler option name
|
|
241
|
+
* @param configPath Optional custom path to tsconfig file
|
|
242
|
+
*/
|
|
243
|
+
static isCompilerOptionEnabled(option: string, configPath?: string): boolean {
|
|
244
|
+
const value = this.getCompilerOption(option, false, configPath);
|
|
245
|
+
return Boolean(value);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Get the output directory (outDir) with proper resolution
|
|
250
|
+
* @param configPath Optional custom path to tsconfig file
|
|
251
|
+
*/
|
|
252
|
+
static getOutDir(configPath?: string): string {
|
|
253
|
+
const outDir = this.getCompilerOption("outDir", "dist", configPath);
|
|
254
|
+
return path.isAbsolute(outDir) ? outDir : path.join(process.cwd(), outDir);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Get the source root directory (rootDir) with proper resolution
|
|
259
|
+
* @param configPath Optional custom path to tsconfig file
|
|
260
|
+
*/
|
|
261
|
+
static getRootDir(configPath?: string): string {
|
|
262
|
+
const rootDir = this.getCompilerOption("rootDir", "src", configPath);
|
|
263
|
+
return path.isAbsolute(rootDir)
|
|
264
|
+
? rootDir
|
|
265
|
+
: path.join(process.cwd(), rootDir);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Get include patterns
|
|
270
|
+
* @param configPath Optional custom path to tsconfig file
|
|
271
|
+
*/
|
|
272
|
+
static getIncludePatterns(configPath?: string): string[] {
|
|
273
|
+
const config = this.loadConfig(configPath);
|
|
274
|
+
return config.include || ["src/**/*"];
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Get exclude patterns
|
|
279
|
+
* @param configPath Optional custom path to tsconfig file
|
|
280
|
+
*/
|
|
281
|
+
static getExcludePatterns(configPath?: string): string[] {
|
|
282
|
+
const config = this.loadConfig(configPath);
|
|
283
|
+
return config.exclude || ["node_modules", "**/*.spec.ts"];
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Check if the project is configured for decorators (required for Fragment)
|
|
288
|
+
*/
|
|
289
|
+
static hasDecoratorSupport(configPath?: string): boolean {
|
|
290
|
+
return (
|
|
291
|
+
this.isCompilerOptionEnabled("experimentalDecorators", configPath) &&
|
|
292
|
+
this.isCompilerOptionEnabled("emitDecoratorMetadata", configPath)
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Resolve a file path based on tsconfig baseUrl and paths
|
|
298
|
+
* @param importPath The import path to resolve
|
|
299
|
+
* @param configPath Optional custom path to tsconfig file
|
|
300
|
+
*/
|
|
301
|
+
static resolvePath(importPath: string, configPath?: string): string | null {
|
|
302
|
+
const compilerOptions = this.getCompilerOptions(configPath);
|
|
303
|
+
|
|
304
|
+
// Handle absolute paths starting with baseUrl
|
|
305
|
+
if (
|
|
306
|
+
compilerOptions.baseUrl &&
|
|
307
|
+
importPath.startsWith(compilerOptions.baseUrl)
|
|
308
|
+
) {
|
|
309
|
+
return path.join(process.cwd(), importPath);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Handle path mappings
|
|
313
|
+
if (compilerOptions.paths && compilerOptions.baseUrl) {
|
|
314
|
+
const baseUrl = path.isAbsolute(compilerOptions.baseUrl)
|
|
315
|
+
? compilerOptions.baseUrl
|
|
316
|
+
: path.join(process.cwd(), compilerOptions.baseUrl);
|
|
317
|
+
|
|
318
|
+
for (const [pattern, paths] of Object.entries(compilerOptions.paths)) {
|
|
319
|
+
// Convert pattern like "@app/*" to regex
|
|
320
|
+
const regexPattern = pattern.replace(/\*/g, "(.*)");
|
|
321
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
322
|
+
const match = importPath.match(regex);
|
|
323
|
+
|
|
324
|
+
if (match) {
|
|
325
|
+
const replacement = match[1] || "";
|
|
326
|
+
if (paths[0]) {
|
|
327
|
+
const resolvedPath = paths[0].replace("*", replacement);
|
|
328
|
+
return path.join(baseUrl, resolvedPath);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Return null if cannot resolve
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Clear the configuration cache (useful for testing)
|
|
340
|
+
*/
|
|
341
|
+
static clearCache(): void {
|
|
342
|
+
this.configCache = null;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Check if tsconfig.json exists
|
|
347
|
+
*/
|
|
348
|
+
static exists(configPath?: string): boolean {
|
|
349
|
+
if (configPath) {
|
|
350
|
+
const fullPath = path.isAbsolute(configPath)
|
|
351
|
+
? configPath
|
|
352
|
+
: path.join(process.cwd(), configPath);
|
|
353
|
+
return fs.existsSync(fullPath);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return this.DEFAULT_TSCONFIG_PATHS.some((defaultPath) =>
|
|
357
|
+
fs.existsSync(path.join(process.cwd(), defaultPath)),
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
}
|