1 | import fs from 'fs';
|
2 | import path from 'path';
|
3 | import importFresh from 'import-fresh';
|
4 | import stripComments from 'strip-json-comments';
|
5 | import yaml from 'js-yaml';
|
6 | import { PROJECT_CONFIG, DEVKIT_CONFIG } from '../../shared/constant';
|
7 |
|
8 | export default class Config {
|
9 | public ctx: any;
|
10 | constructor(ctx: any) {
|
11 | this.ctx = ctx;
|
12 | }
|
13 |
|
14 | getProjectDirectory(): string {
|
15 | let currDir: string = process.cwd();
|
16 |
|
17 | const isConfigExits = () => {
|
18 | for (const filename of PROJECT_CONFIG) {
|
19 | if (fs.existsSync(path.join(currDir, filename))) {
|
20 | return true;
|
21 | }
|
22 | }
|
23 | return false;
|
24 | };
|
25 |
|
26 | while (!isConfigExits()) {
|
27 | currDir = path.join(currDir, '../');
|
28 | if (currDir === '/' || /^[a-zA-Z]:\\$/.test(currDir)) {
|
29 | return '';
|
30 | }
|
31 | }
|
32 |
|
33 | return currDir;
|
34 | }
|
35 |
|
36 | loadProjectConfig() {
|
37 | const directoryPath = this.getProjectDirectory();
|
38 | return this.loadConfig(directoryPath, PROJECT_CONFIG);
|
39 | }
|
40 |
|
41 | loadDevkitConfig(directoryPath: string) {
|
42 | return this.loadConfig(directoryPath, DEVKIT_CONFIG);
|
43 | }
|
44 |
|
45 | loadConfig(directoryPath: string, configArray: Array<string>) {
|
46 | for (const filename of configArray) {
|
47 | const filePath = path.join(directoryPath, filename);
|
48 | if (fs.existsSync(filePath)) {
|
49 | let configData;
|
50 |
|
51 | try {
|
52 | configData = this.loadConfigFile(filePath);
|
53 | } catch (error) {
|
54 | if (!error || error.code !== 'FEFLOW_CONFIG_FIELD_NOT_FOUND') {
|
55 | throw error;
|
56 | }
|
57 | }
|
58 |
|
59 | if (configData) {
|
60 | this.ctx.logger.debug('Config file found', filePath);
|
61 | this.ctx.logger.debug('Config data', configData);
|
62 |
|
63 | return configData;
|
64 | }
|
65 | }
|
66 | }
|
67 |
|
68 | this.ctx.logger.debug(`Config file not found.`);
|
69 | return null;
|
70 | }
|
71 |
|
72 | loadConfigFile(filePath: string) {
|
73 | switch (path.extname(filePath)) {
|
74 | case '.js':
|
75 | return this.loadJSConfigFile(filePath);
|
76 |
|
77 | case '.json':
|
78 | if (path.basename(filePath) === 'package.json') {
|
79 | return this.loadPackageJSONConfigFile(filePath);
|
80 | }
|
81 | return this.loadJSONConfigFile(filePath);
|
82 |
|
83 | case '.yaml':
|
84 | case '.yml':
|
85 | return this.loadYAMLConfigFile(filePath);
|
86 |
|
87 | default:
|
88 | return this.loadLegacyConfigFile(filePath);
|
89 | }
|
90 | }
|
91 |
|
92 | loadJSConfigFile(filePath: string) {
|
93 | this.ctx.logger.debug(`Loading JS config file: ${filePath}`);
|
94 | try {
|
95 | return importFresh(filePath);
|
96 | } catch (e) {
|
97 | this.ctx.logger.debug(`Error reading JavaScript file: ${filePath}`);
|
98 | e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`;
|
99 | throw e;
|
100 | }
|
101 | }
|
102 |
|
103 | loadYAMLConfigFile(filePath: string) {
|
104 | this.ctx.logger.debug(`Loading YAML config file: ${filePath}`);
|
105 | try {
|
106 | return yaml.safeLoad(this.readFile(filePath)) || {};
|
107 | } catch (e) {
|
108 | this.ctx.logger.debug(`Error reading YAML file: ${filePath}`);
|
109 | e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`;
|
110 | throw e;
|
111 | }
|
112 | }
|
113 |
|
114 | loadPackageJSONConfigFile(filePath: string) {
|
115 | this.ctx.logger.debug(`Loading package.json config file: ${filePath}`);
|
116 | try {
|
117 | const packageData = this.loadJSONConfigFile(filePath);
|
118 |
|
119 | if (!Object.hasOwnProperty.call(packageData, 'feflowConfig')) {
|
120 | throw Object.assign(
|
121 | new Error("package.json file doesn't have 'feflowConfig' field."),
|
122 | { code: 'FEFLOW_CONFIG_FIELD_NOT_FOUND' }
|
123 | );
|
124 | }
|
125 |
|
126 | return packageData.feflowConfig;
|
127 | } catch (e) {
|
128 | this.ctx.logger.debug(`Error reading package.json file: ${filePath}`);
|
129 | e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`;
|
130 | throw e;
|
131 | }
|
132 | }
|
133 |
|
134 | loadJSONConfigFile(filePath: string) {
|
135 | this.ctx.logger.debug(`Loading JSON config file: ${filePath}`);
|
136 |
|
137 | try {
|
138 | return JSON.parse(stripComments(this.readFile(filePath)));
|
139 | } catch (e) {
|
140 | this.ctx.logger.debug(`Error reading JSON file: ${filePath}`);
|
141 | e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`;
|
142 | e.messageTemplate = 'failed-to-read-json';
|
143 | e.messageData = {
|
144 | path: filePath,
|
145 | message: e.message
|
146 | };
|
147 | throw e;
|
148 | }
|
149 | }
|
150 |
|
151 | loadLegacyConfigFile(filePath: string) {
|
152 | this.ctx.logger.debug(`Loading legacy config file: ${filePath}`);
|
153 | try {
|
154 | return yaml.safeLoad(stripComments(this.readFile(filePath))) || {};
|
155 | } catch (e) {
|
156 | this.ctx.logger.debug('Error reading YAML file: %s\n%o', filePath, e);
|
157 | e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`;
|
158 | throw e;
|
159 | }
|
160 | }
|
161 |
|
162 | readFile(filePath: string) {
|
163 | return fs.readFileSync(filePath, 'utf8').replace(/^\ufeff/u, '');
|
164 | }
|
165 | }
|