@featurevisor/core 0.53.5 → 0.54.0
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/.eslintcache +1 -1
- package/CHANGELOG.md +11 -0
- package/coverage/clover.xml +2 -2
- package/coverage/lcov-report/index.html +1 -1
- package/coverage/lcov-report/lib/builder/allocator.js.html +1 -1
- package/coverage/lcov-report/lib/builder/index.html +1 -1
- package/coverage/lcov-report/lib/builder/traffic.js.html +1 -1
- package/coverage/lcov-report/src/builder/allocator.ts.html +1 -1
- package/coverage/lcov-report/src/builder/index.html +1 -1
- package/coverage/lcov-report/src/builder/traffic.ts.html +1 -1
- package/lib/config/index.d.ts +2 -0
- package/lib/config/index.js +19 -0
- package/lib/config/index.js.map +1 -0
- package/lib/{datasource → config}/parsers.d.ts +3 -3
- package/lib/config/parsers.js +32 -0
- package/lib/config/parsers.js.map +1 -0
- package/lib/{config.d.ts → config/projectConfig.d.ts} +2 -1
- package/lib/{config.js → config/projectConfig.js} +16 -5
- package/lib/config/projectConfig.js.map +1 -0
- package/lib/datasource/adapter.d.ts +10 -0
- package/lib/datasource/adapter.js +10 -0
- package/lib/datasource/adapter.js.map +1 -0
- package/lib/datasource/datasource.d.ts +11 -18
- package/lib/datasource/datasource.js +32 -130
- package/lib/datasource/datasource.js.map +1 -1
- package/lib/datasource/filesystemAdapter.d.ts +20 -0
- package/lib/datasource/filesystemAdapter.js +173 -0
- package/lib/datasource/filesystemAdapter.js.map +1 -0
- package/lib/datasource/index.d.ts +2 -1
- package/lib/datasource/index.js +2 -1
- package/lib/datasource/index.js.map +1 -1
- package/lib/{init.js → init/index.js} +1 -1
- package/lib/init/index.js.map +1 -0
- package/lib/linter/checkCircularDependency.js +1 -1
- package/lib/linter/checkCircularDependency.js.map +1 -1
- package/lib/{restore.d.ts → restore/index.d.ts} +1 -1
- package/lib/{restore.js → restore/index.js} +1 -1
- package/lib/restore/index.js.map +1 -0
- package/lib/tester/testProject.js +1 -1
- package/lib/tester/testProject.js.map +1 -1
- package/lib/tester/testSegment.js +1 -1
- package/lib/tester/testSegment.js.map +1 -1
- package/package.json +5 -5
- package/src/config/index.ts +2 -0
- package/src/config/parsers.ts +40 -0
- package/src/{config.ts → config/projectConfig.ts} +20 -5
- package/src/datasource/adapter.ts +15 -0
- package/src/datasource/datasource.ts +44 -131
- package/src/datasource/filesystemAdapter.ts +108 -0
- package/src/datasource/index.ts +2 -1
- package/src/linter/checkCircularDependency.ts +1 -1
- package/src/{restore.ts → restore/index.ts} +1 -1
- package/src/tester/testProject.ts +1 -1
- package/src/tester/testSegment.ts +1 -1
- package/lib/config.js.map +0 -1
- package/lib/datasource/parsers.js +0 -19
- package/lib/datasource/parsers.js.map +0 -1
- package/lib/init.js.map +0 -1
- package/lib/restore.js.map +0 -1
- package/src/datasource/parsers.ts +0 -26
- /package/lib/{init.d.ts → init/index.d.ts} +0 -0
- /package/src/{init.ts → init/index.ts} +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as YAML from "js-yaml";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* If we want to add more built-in parsers,
|
|
5
|
+
* add them to this object with new file extension as the key,
|
|
6
|
+
* and a function that takes file content as string and returns parsed object as the value.
|
|
7
|
+
*/
|
|
8
|
+
export const parsers: { [key: string]: CustomParser } = {
|
|
9
|
+
// YAML
|
|
10
|
+
yml: {
|
|
11
|
+
extension: "yml",
|
|
12
|
+
parse: function <T>(content: string): T {
|
|
13
|
+
return YAML.load(content) as T;
|
|
14
|
+
},
|
|
15
|
+
stringify: function (content: any) {
|
|
16
|
+
return YAML.dump(content);
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
// JSON
|
|
21
|
+
json: {
|
|
22
|
+
extension: "json",
|
|
23
|
+
parse: function <T>(content: string): T {
|
|
24
|
+
return JSON.parse(content) as T;
|
|
25
|
+
},
|
|
26
|
+
stringify: function (content: any) {
|
|
27
|
+
return JSON.stringify(content, null, 2);
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type BuiltInParser = keyof typeof parsers; // keys of parsers object
|
|
33
|
+
|
|
34
|
+
export interface CustomParser {
|
|
35
|
+
extension: string;
|
|
36
|
+
parse: <T>(content: string) => T;
|
|
37
|
+
stringify: (content: any) => string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type Parser = BuiltInParser | CustomParser;
|
|
@@ -2,7 +2,8 @@ import * as path from "path";
|
|
|
2
2
|
|
|
3
3
|
import { BucketBy } from "@featurevisor/types";
|
|
4
4
|
|
|
5
|
-
import { Parser } from "./
|
|
5
|
+
import { Parser, parsers } from "./parsers";
|
|
6
|
+
import { FilesystemAdapter } from "../datasource/filesystemAdapter";
|
|
6
7
|
|
|
7
8
|
export const FEATURES_DIRECTORY_NAME = "features";
|
|
8
9
|
export const SEGMENTS_DIRECTORY_NAME = "segments";
|
|
@@ -42,6 +43,7 @@ export interface ProjectConfig {
|
|
|
42
43
|
prettyState: boolean;
|
|
43
44
|
prettyDatafile: boolean;
|
|
44
45
|
siteExportDirectoryPath: string;
|
|
46
|
+
adapter: any; // @TODO: type this properly later
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
// rootDirectoryPath: path to the root directory of the project (without ending with a slash)
|
|
@@ -66,20 +68,33 @@ export function getProjectConfig(rootDirectoryPath: string): ProjectConfig {
|
|
|
66
68
|
prettyDatafile: DEFAULT_PRETTY_DATAFILE,
|
|
67
69
|
|
|
68
70
|
siteExportDirectoryPath: path.join(rootDirectoryPath, SITE_EXPORT_DIRECTORY_NAME),
|
|
71
|
+
|
|
72
|
+
adapter: FilesystemAdapter,
|
|
69
73
|
};
|
|
70
74
|
|
|
71
75
|
const configModulePath = path.join(rootDirectoryPath, CONFIG_MODULE_NAME);
|
|
72
76
|
const customConfig = require(configModulePath);
|
|
73
77
|
|
|
74
|
-
const
|
|
78
|
+
const mergedConfig = {};
|
|
75
79
|
|
|
76
80
|
Object.keys(baseConfig).forEach((key) => {
|
|
77
|
-
|
|
81
|
+
mergedConfig[key] = customConfig[key] || baseConfig[key];
|
|
78
82
|
|
|
79
|
-
if (key.endsWith("Path") &&
|
|
80
|
-
|
|
83
|
+
if (key.endsWith("Path") && mergedConfig[key].indexOf(ROOT_DIR_PLACEHOLDER) !== -1) {
|
|
84
|
+
mergedConfig[key] = mergedConfig[key].replace(ROOT_DIR_PLACEHOLDER, rootDirectoryPath);
|
|
81
85
|
}
|
|
82
86
|
});
|
|
83
87
|
|
|
88
|
+
const finalConfig = mergedConfig as ProjectConfig;
|
|
89
|
+
|
|
90
|
+
if (typeof finalConfig.parser === "string") {
|
|
91
|
+
const allowedParsers = Object.keys(parsers);
|
|
92
|
+
if (allowedParsers.indexOf(finalConfig.parser) === -1) {
|
|
93
|
+
throw new Error(`Invalid parser: ${finalConfig.parser}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
finalConfig.parser = parsers[finalConfig.parser];
|
|
97
|
+
}
|
|
98
|
+
|
|
84
99
|
return finalConfig as ProjectConfig;
|
|
85
100
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { EnvironmentKey, ExistingState } from "@featurevisor/types";
|
|
2
|
+
|
|
3
|
+
export type EntityType = "feature" | "group" | "segment" | "attribute" | "test";
|
|
4
|
+
|
|
5
|
+
export abstract class Adapter {
|
|
6
|
+
// entities
|
|
7
|
+
abstract listEntities(entityType: EntityType): Promise<string[]>;
|
|
8
|
+
abstract entityExists(entityType: EntityType, entityKey: string): Promise<boolean>;
|
|
9
|
+
abstract readEntity(entityType: EntityType, entityKey: string): Promise<string>;
|
|
10
|
+
abstract parseEntity<T>(entityType: EntityType, entityKey: string): Promise<T>;
|
|
11
|
+
|
|
12
|
+
// state
|
|
13
|
+
abstract readState(environment: EnvironmentKey): Promise<ExistingState>;
|
|
14
|
+
abstract writeState(environment: EnvironmentKey, existingState: ExistingState): Promise<void>;
|
|
15
|
+
}
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
import * as path from "path";
|
|
2
|
-
import * as fs from "fs";
|
|
3
|
-
|
|
4
|
-
import * as mkdirp from "mkdirp";
|
|
5
|
-
|
|
6
1
|
import {
|
|
7
2
|
ParsedFeature,
|
|
8
3
|
Segment,
|
|
@@ -12,137 +7,35 @@ import {
|
|
|
12
7
|
Test,
|
|
13
8
|
EnvironmentKey,
|
|
14
9
|
ExistingState,
|
|
10
|
+
SegmentKey,
|
|
11
|
+
AttributeKey,
|
|
15
12
|
} from "@featurevisor/types";
|
|
16
13
|
|
|
17
|
-
import { ProjectConfig } from "../config";
|
|
18
|
-
import { parsers } from "./parsers";
|
|
19
|
-
|
|
20
|
-
export type EntityType = "feature" | "group" | "segment" | "attribute" | "test";
|
|
14
|
+
import { ProjectConfig, CustomParser } from "../config";
|
|
21
15
|
|
|
22
|
-
|
|
23
|
-
projectConfig: ProjectConfig,
|
|
24
|
-
environment: EnvironmentKey,
|
|
25
|
-
): string {
|
|
26
|
-
return path.join(projectConfig.stateDirectoryPath, `existing-state-${environment}.json`);
|
|
27
|
-
}
|
|
16
|
+
import { Adapter } from "./adapter";
|
|
28
17
|
|
|
29
18
|
export class Datasource {
|
|
30
|
-
private
|
|
31
|
-
private parse;
|
|
19
|
+
private adapter: Adapter;
|
|
32
20
|
|
|
33
21
|
constructor(private config: ProjectConfig) {
|
|
34
|
-
|
|
35
|
-
// built-in parsers
|
|
36
|
-
if (typeof parsers[config.parser] !== "function") {
|
|
37
|
-
throw new Error(`Invalid parser: ${config.parser}`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
this.extension = config.parser;
|
|
41
|
-
this.parse = parsers[config.parser];
|
|
42
|
-
} else if (typeof config.parser === "object") {
|
|
43
|
-
// custom parser
|
|
44
|
-
if (typeof config.parser.extension !== "string") {
|
|
45
|
-
throw new Error(`Invalid parser extension: ${config.parser.extension}`);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (typeof config.parser.parse !== "function") {
|
|
49
|
-
throw new Error(`Invalid parser parse function: ${config.parser.parse}`);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
this.extension = config.parser.extension;
|
|
53
|
-
this.parse = config.parser.parse;
|
|
54
|
-
} else {
|
|
55
|
-
throw new Error(`Invalid parser: ${config.parser}`);
|
|
56
|
-
}
|
|
22
|
+
this.adapter = new config.adapter(config);
|
|
57
23
|
}
|
|
58
24
|
|
|
25
|
+
// @TODO: only site generator needs it, find a way to get it out of here later
|
|
59
26
|
getExtension() {
|
|
60
|
-
return this.extension;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Common methods for entities
|
|
65
|
-
*/
|
|
66
|
-
async listEntities(entityType: EntityType): Promise<string[]> {
|
|
67
|
-
const directoryPath = this.getEntityDirectoryPath(entityType);
|
|
68
|
-
|
|
69
|
-
if (!fs.existsSync(directoryPath)) {
|
|
70
|
-
return [];
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return fs
|
|
74
|
-
.readdirSync(directoryPath)
|
|
75
|
-
.filter((fileName) => fileName.endsWith(`.${this.extension}`))
|
|
76
|
-
.map((fileName) => fileName.replace(`.${this.extension}`, ""));
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
getEntityDirectoryPath(entityType: EntityType): string {
|
|
80
|
-
if (entityType === "feature") {
|
|
81
|
-
return this.config.featuresDirectoryPath;
|
|
82
|
-
} else if (entityType === "group") {
|
|
83
|
-
return this.config.groupsDirectoryPath;
|
|
84
|
-
} else if (entityType === "segment") {
|
|
85
|
-
return this.config.segmentsDirectoryPath;
|
|
86
|
-
} else if (entityType === "test") {
|
|
87
|
-
return this.config.testsDirectoryPath;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return this.config.attributesDirectoryPath;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
getEntityPath(entityType: EntityType, entityKey: string): string {
|
|
94
|
-
const basePath = this.getEntityDirectoryPath(entityType);
|
|
95
|
-
|
|
96
|
-
return path.join(basePath, `${entityKey}.${this.extension}`);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async entityExists(entityType: EntityType, entityKey: string): Promise<boolean> {
|
|
100
|
-
const entityPath = this.getEntityPath(entityType, entityKey);
|
|
101
|
-
|
|
102
|
-
return fs.existsSync(entityPath);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async readEntity(entityType: EntityType, entityKey: string): Promise<string> {
|
|
106
|
-
const filePath = this.getEntityPath(entityType, entityKey);
|
|
107
|
-
|
|
108
|
-
return fs.readFileSync(filePath, "utf8");
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async parseEntity<T>(entityType: EntityType, entityKey: string): Promise<T> {
|
|
112
|
-
const entityContent = await this.readEntity(entityType, entityKey);
|
|
113
|
-
|
|
114
|
-
return this.parse(entityContent) as T;
|
|
27
|
+
return (this.config.parser as CustomParser).extension;
|
|
115
28
|
}
|
|
116
29
|
|
|
117
30
|
/**
|
|
118
31
|
* State
|
|
119
32
|
*/
|
|
120
33
|
async readState(environment: EnvironmentKey): Promise<ExistingState> {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (!fs.existsSync(filePath)) {
|
|
124
|
-
return {
|
|
125
|
-
features: {},
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return require(filePath);
|
|
34
|
+
return this.adapter.readState(environment);
|
|
130
35
|
}
|
|
131
36
|
|
|
132
37
|
async writeState(environment: EnvironmentKey, existingState: ExistingState) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (!fs.existsSync(this.config.stateDirectoryPath)) {
|
|
136
|
-
mkdirp.sync(this.config.stateDirectoryPath);
|
|
137
|
-
}
|
|
138
|
-
fs.writeFileSync(
|
|
139
|
-
filePath,
|
|
140
|
-
this.config.prettyState
|
|
141
|
-
? JSON.stringify(existingState, null, 2)
|
|
142
|
-
: JSON.stringify(existingState),
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
fs.writeFileSync(filePath, JSON.stringify(existingState, null, 2));
|
|
38
|
+
return this.adapter.writeState(environment, existingState);
|
|
146
39
|
}
|
|
147
40
|
|
|
148
41
|
/**
|
|
@@ -151,11 +44,15 @@ export class Datasource {
|
|
|
151
44
|
|
|
152
45
|
// features
|
|
153
46
|
async listFeatures() {
|
|
154
|
-
return await this.listEntities("feature");
|
|
47
|
+
return await this.adapter.listEntities("feature");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
featureExists(featureKey: FeatureKey) {
|
|
51
|
+
return this.adapter.entityExists("feature", featureKey);
|
|
155
52
|
}
|
|
156
53
|
|
|
157
|
-
readFeature(featureKey:
|
|
158
|
-
return this.parseEntity<ParsedFeature>("feature", featureKey);
|
|
54
|
+
readFeature(featureKey: FeatureKey) {
|
|
55
|
+
return this.adapter.parseEntity<ParsedFeature>("feature", featureKey);
|
|
159
56
|
}
|
|
160
57
|
|
|
161
58
|
async getRequiredFeaturesChain(
|
|
@@ -164,7 +61,7 @@ export class Datasource {
|
|
|
164
61
|
): Promise<Set<FeatureKey>> {
|
|
165
62
|
chain.add(featureKey);
|
|
166
63
|
|
|
167
|
-
if (!this.entityExists("feature", featureKey)) {
|
|
64
|
+
if (!this.adapter.entityExists("feature", featureKey)) {
|
|
168
65
|
throw new Error(`Feature not found: ${featureKey}`);
|
|
169
66
|
}
|
|
170
67
|
|
|
@@ -189,37 +86,53 @@ export class Datasource {
|
|
|
189
86
|
|
|
190
87
|
// segments
|
|
191
88
|
listSegments() {
|
|
192
|
-
return this.listEntities("segment");
|
|
89
|
+
return this.adapter.listEntities("segment");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
segmentExists(segmentKey: SegmentKey) {
|
|
93
|
+
return this.adapter.entityExists("segment", segmentKey);
|
|
193
94
|
}
|
|
194
95
|
|
|
195
|
-
readSegment(segmentKey:
|
|
196
|
-
return this.parseEntity<Segment>("segment", segmentKey);
|
|
96
|
+
readSegment(segmentKey: SegmentKey) {
|
|
97
|
+
return this.adapter.parseEntity<Segment>("segment", segmentKey);
|
|
197
98
|
}
|
|
198
99
|
|
|
199
100
|
// attributes
|
|
200
101
|
listAttributes() {
|
|
201
|
-
return this.listEntities("attribute");
|
|
102
|
+
return this.adapter.listEntities("attribute");
|
|
202
103
|
}
|
|
203
104
|
|
|
204
|
-
|
|
205
|
-
return this.
|
|
105
|
+
attributeExists(attributeKey: AttributeKey) {
|
|
106
|
+
return this.adapter.entityExists("attribute", attributeKey);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
readAttribute(attributeKey: AttributeKey) {
|
|
110
|
+
return this.adapter.parseEntity<Attribute>("attribute", attributeKey);
|
|
206
111
|
}
|
|
207
112
|
|
|
208
113
|
// groups
|
|
209
114
|
async listGroups() {
|
|
210
|
-
return this.listEntities("group");
|
|
115
|
+
return this.adapter.listEntities("group");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
groupExists(groupKey: string) {
|
|
119
|
+
return this.adapter.entityExists("group", groupKey);
|
|
211
120
|
}
|
|
212
121
|
|
|
213
122
|
readGroup(groupKey: string) {
|
|
214
|
-
return this.parseEntity<Group>("group", groupKey);
|
|
123
|
+
return this.adapter.parseEntity<Group>("group", groupKey);
|
|
215
124
|
}
|
|
216
125
|
|
|
217
126
|
// tests
|
|
218
127
|
listTests() {
|
|
219
|
-
return this.listEntities("test");
|
|
128
|
+
return this.adapter.listEntities("test");
|
|
220
129
|
}
|
|
221
130
|
|
|
222
131
|
readTest(testKey: string) {
|
|
223
|
-
return this.parseEntity<Test>("test", testKey);
|
|
132
|
+
return this.adapter.parseEntity<Test>("test", testKey);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
getTestSpecName(testKey: string) {
|
|
136
|
+
return `${testKey}.${this.getExtension()}`;
|
|
224
137
|
}
|
|
225
138
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
|
|
4
|
+
import * as mkdirp from "mkdirp";
|
|
5
|
+
|
|
6
|
+
import { ExistingState, EnvironmentKey } from "@featurevisor/types";
|
|
7
|
+
|
|
8
|
+
import { Adapter, EntityType } from "./adapter";
|
|
9
|
+
import { ProjectConfig, CustomParser } from "../config";
|
|
10
|
+
|
|
11
|
+
export function getExistingStateFilePath(
|
|
12
|
+
projectConfig: ProjectConfig,
|
|
13
|
+
environment: EnvironmentKey,
|
|
14
|
+
): string {
|
|
15
|
+
return path.join(projectConfig.stateDirectoryPath, `existing-state-${environment}.json`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class FilesystemAdapter extends Adapter {
|
|
19
|
+
private parser: CustomParser;
|
|
20
|
+
|
|
21
|
+
constructor(private config: ProjectConfig) {
|
|
22
|
+
super();
|
|
23
|
+
|
|
24
|
+
this.parser = config.parser as CustomParser;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getEntityDirectoryPath(entityType: EntityType): string {
|
|
28
|
+
if (entityType === "feature") {
|
|
29
|
+
return this.config.featuresDirectoryPath;
|
|
30
|
+
} else if (entityType === "group") {
|
|
31
|
+
return this.config.groupsDirectoryPath;
|
|
32
|
+
} else if (entityType === "segment") {
|
|
33
|
+
return this.config.segmentsDirectoryPath;
|
|
34
|
+
} else if (entityType === "test") {
|
|
35
|
+
return this.config.testsDirectoryPath;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return this.config.attributesDirectoryPath;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getEntityPath(entityType: EntityType, entityKey: string): string {
|
|
42
|
+
const basePath = this.getEntityDirectoryPath(entityType);
|
|
43
|
+
|
|
44
|
+
return path.join(basePath, `${entityKey}.${this.parser.extension}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async listEntities(entityType: EntityType): Promise<string[]> {
|
|
48
|
+
const directoryPath = this.getEntityDirectoryPath(entityType);
|
|
49
|
+
|
|
50
|
+
if (!fs.existsSync(directoryPath)) {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return fs
|
|
55
|
+
.readdirSync(directoryPath)
|
|
56
|
+
.filter((fileName) => fileName.endsWith(`.${this.parser.extension}`))
|
|
57
|
+
.map((fileName) => fileName.replace(`.${this.parser.extension}`, ""));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async entityExists(entityType: EntityType, entityKey: string): Promise<boolean> {
|
|
61
|
+
const entityPath = this.getEntityPath(entityType, entityKey);
|
|
62
|
+
|
|
63
|
+
return fs.existsSync(entityPath);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async readEntity(entityType: EntityType, entityKey: string): Promise<string> {
|
|
67
|
+
const filePath = this.getEntityPath(entityType, entityKey);
|
|
68
|
+
|
|
69
|
+
return fs.readFileSync(filePath, "utf8");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async parseEntity<T>(entityType: EntityType, entityKey: string): Promise<T> {
|
|
73
|
+
const entityContent = await this.readEntity(entityType, entityKey);
|
|
74
|
+
|
|
75
|
+
return this.parser.parse<T>(entityContent);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* State
|
|
80
|
+
*/
|
|
81
|
+
async readState(environment: EnvironmentKey): Promise<ExistingState> {
|
|
82
|
+
const filePath = getExistingStateFilePath(this.config, environment);
|
|
83
|
+
|
|
84
|
+
if (!fs.existsSync(filePath)) {
|
|
85
|
+
return {
|
|
86
|
+
features: {},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return require(filePath);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async writeState(environment: EnvironmentKey, existingState: ExistingState) {
|
|
94
|
+
const filePath = getExistingStateFilePath(this.config, environment);
|
|
95
|
+
|
|
96
|
+
if (!fs.existsSync(this.config.stateDirectoryPath)) {
|
|
97
|
+
mkdirp.sync(this.config.stateDirectoryPath);
|
|
98
|
+
}
|
|
99
|
+
fs.writeFileSync(
|
|
100
|
+
filePath,
|
|
101
|
+
this.config.prettyState
|
|
102
|
+
? JSON.stringify(existingState, null, 2)
|
|
103
|
+
: JSON.stringify(existingState),
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
fs.writeFileSync(filePath, JSON.stringify(existingState, null, 2));
|
|
107
|
+
}
|
|
108
|
+
}
|
package/src/datasource/index.ts
CHANGED
|
@@ -25,7 +25,7 @@ export async function checkForCircularDependencyInRequired(
|
|
|
25
25
|
throw new Error(`circular dependency found: ${chain.join(" -> ")}`);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const requiredFeatureExists = await datasource.
|
|
28
|
+
const requiredFeatureExists = await datasource.featureExists(featureKey);
|
|
29
29
|
|
|
30
30
|
if (!requiredFeatureExists) {
|
|
31
31
|
throw new Error(`required feature "${requiredKey}" not found`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as path from "path";
|
|
2
2
|
import { execSync } from "child_process";
|
|
3
3
|
|
|
4
|
-
import { Dependencies } from "
|
|
4
|
+
import { Dependencies } from "../dependencies";
|
|
5
5
|
|
|
6
6
|
export async function restoreProject(deps: Dependencies) {
|
|
7
7
|
const { rootDirectoryPath, projectConfig } = deps;
|
|
@@ -29,7 +29,7 @@ export async function testProject(deps: Dependencies): Promise<boolean> {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
for (const testFile of testFiles) {
|
|
32
|
-
const testFilePath = datasource.
|
|
32
|
+
const testFilePath = datasource.getTestSpecName(testFile);
|
|
33
33
|
|
|
34
34
|
console.log("");
|
|
35
35
|
console.log(CLI_FORMAT_BOLD, `Testing: ${testFilePath.replace(rootDirectoryPath, "")}`);
|
|
@@ -12,7 +12,7 @@ export async function testSegment(datasource: Datasource, test: TestSegment): Pr
|
|
|
12
12
|
|
|
13
13
|
console.log(CLI_FORMAT_BOLD, ` Segment "${segmentKey}":`);
|
|
14
14
|
|
|
15
|
-
const segmentExists = await datasource.
|
|
15
|
+
const segmentExists = await datasource.segmentExists(segmentKey);
|
|
16
16
|
|
|
17
17
|
if (!segmentExists) {
|
|
18
18
|
console.error(CLI_FORMAT_RED, ` Segment does not exist: ${segmentKey}`);
|
package/lib/config.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;AAAA,2BAA6B;AAMhB,QAAA,uBAAuB,GAAG,UAAU,CAAC;AACrC,QAAA,uBAAuB,GAAG,UAAU,CAAC;AACrC,QAAA,yBAAyB,GAAG,YAAY,CAAC;AACzC,QAAA,qBAAqB,GAAG,QAAQ,CAAC;AACjC,QAAA,oBAAoB,GAAG,OAAO,CAAC;AAC/B,QAAA,oBAAoB,GAAG,eAAe,CAAC;AACvC,QAAA,qBAAqB,GAAG,MAAM,CAAC;AAC/B,QAAA,0BAA0B,GAAG,KAAK,CAAC;AAEnC,QAAA,kBAAkB,GAAG,wBAAwB,CAAC;AAC9C,QAAA,oBAAoB,GAAG,WAAW,CAAC;AAEnC,QAAA,oBAAoB,GAAG,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;AACjD,QAAA,YAAY,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,QAAA,2BAA2B,GAAG,QAAQ,CAAC;AAEvC,QAAA,oBAAoB,GAAG,KAAK,CAAC;AAC7B,QAAA,uBAAuB,GAAG,KAAK,CAAC;AAEhC,QAAA,cAAc,GAAW,KAAK,CAAC;AAE/B,QAAA,cAAc,GAAG,GAAG,CAAC;AAmBlC,6FAA6F;AAC7F,SAAgB,gBAAgB,CAAC,iBAAyB;IACxD,IAAM,UAAU,GAAkB;QAChC,qBAAqB,EAAE,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,+BAAuB,CAAC;QAC5E,qBAAqB,EAAE,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,+BAAuB,CAAC;QAC5E,uBAAuB,EAAE,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,iCAAyB,CAAC;QAChF,mBAAmB,EAAE,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,6BAAqB,CAAC;QACxE,kBAAkB,EAAE,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,4BAAoB,CAAC;QAEtE,kBAAkB,EAAE,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,4BAAoB,CAAC;QACtE,mBAAmB,EAAE,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,6BAAqB,CAAC;QAExE,YAAY,EAAE,4BAAoB;QAClC,IAAI,EAAE,oBAAY;QAClB,eAAe,EAAE,QAAQ;QAEzB,MAAM,EAAE,sBAAc;QAEtB,WAAW,EAAE,4BAAoB;QACjC,cAAc,EAAE,+BAAuB;QAEvC,uBAAuB,EAAE,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,kCAA0B,CAAC;KAClF,CAAC;IAEF,IAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,0BAAkB,CAAC,CAAC;IAC1E,IAAM,YAAY,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAE/C,IAAM,WAAW,GAAG,EAAE,CAAC;IAEvB,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,UAAC,GAAG;QAClC,WAAW,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;QAExD,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,4BAAoB,CAAC,KAAK,CAAC,CAAC,EAAE;YACjF,WAAW,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,4BAAoB,EAAE,iBAAiB,CAAC,CAAC;SACtF;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,WAA4B,CAAC;AACtC,CAAC;AArCD,4CAqCC"}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parsers = void 0;
|
|
4
|
-
var YAML = require("js-yaml");
|
|
5
|
-
/**
|
|
6
|
-
* If we want to add more built-in parsers,
|
|
7
|
-
* add them to this object with new file extension as the key,
|
|
8
|
-
* and a function that takes file content as string and returns parsed object as the value.
|
|
9
|
-
*/
|
|
10
|
-
exports.parsers = {
|
|
11
|
-
// extension => function
|
|
12
|
-
yml: function (content) {
|
|
13
|
-
return YAML.load(content);
|
|
14
|
-
},
|
|
15
|
-
json: function (content) {
|
|
16
|
-
return JSON.parse(content);
|
|
17
|
-
},
|
|
18
|
-
};
|
|
19
|
-
//# sourceMappingURL=parsers.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"parsers.js","sourceRoot":"","sources":["../../src/datasource/parsers.ts"],"names":[],"mappings":";;;AAAA,8BAAgC;AAEhC;;;;GAIG;AACU,QAAA,OAAO,GAAG;IACrB,wBAAwB;IACxB,GAAG,YAAC,OAAe;QACjB,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5B,CAAC;IAED,IAAI,YAAC,OAAe;QAClB,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;CACF,CAAC"}
|
package/lib/init.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":";;;AAAA,+BAA0B;AAC1B,yBAA2B;AAEd,QAAA,eAAe,GAAG,aAAa,CAAC;AAEhC,QAAA,iBAAiB,GAAG,SAAS,CAAC;AAC9B,QAAA,kBAAkB,GAAG,cAAc,CAAC;AACpC,QAAA,oBAAoB,GAAG,MAAM,CAAC;AAE9B,QAAA,gBAAgB,GAAG,sCAA+B,yBAAiB,cAAI,0BAAkB,qBAAW,4BAAoB,CAAE,CAAC;AAExI,SAAS,cAAc,CAAC,WAAmB;IACzC,OAAO,UAAG,0BAAkB,cAAI,4BAAoB,uBAAa,WAAW,MAAG,CAAC;AAClF,CAAC;AAED,SAAgB,WAAW,CACzB,aAAqB,EACrB,WAAqC;IAArC,4BAAA,EAAA,cAAsB,uBAAe;IAErC,OAAO,IAAI,OAAO,CAAC,UAAU,OAAO;QAClC,eAAK,CAAC,GAAG,CAAC,wBAAgB,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,UAAC,QAAQ;YACpE,QAAQ,CAAC,IAAI;iBACV,IAAI,CACH,GAAG,CAAC,CAAC,CAAC;gBACJ,CAAC,EAAE,aAAa;gBAChB,MAAM,EAAE,UAAC,IAAI,IAAK,OAAA,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,EAA/C,CAA+C;gBACjE,KAAK,EAAE,CAAC;aACT,CAAC,CACH;iBACA,EAAE,CAAC,OAAO,EAAE,UAAC,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBAEjB,OAAO,CAAC,KAAK,CAAC,CAAC;YACjB,CAAC,CAAC;iBACD,EAAE,CAAC,QAAQ,EAAE;gBACZ,OAAO,CAAC,GAAG,CAAC,gCAAyB,aAAa,CAAE,CAAC,CAAC;gBACtD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAChB,OAAO,CAAC,GAAG,CAAC,oDAAkD,CAAC,CAAC;gBAEhE,OAAO,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AA5BD,kCA4BC"}
|
package/lib/restore.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"restore.js","sourceRoot":"","sources":["../src/restore.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2BAA6B;AAC7B,+CAAyC;AAIzC,SAAsB,cAAc,CAAC,IAAkB;;;;YAC7C,iBAAiB,GAAoB,IAAI,kBAAxB,EAAE,aAAa,GAAK,IAAI,cAAT,CAAU;YAE5C,oBAAoB,GAAG,IAAI,CAAC,QAAQ,CAAC,iBAAiB,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;YAC1F,GAAG,GAAG,0BAAmB,oBAAoB,SAAG,IAAI,CAAC,GAAG,CAAE,CAAC;YAEjE,IAAI;gBACF,IAAA,wBAAQ,EAAC,GAAG,EAAE;oBACZ,GAAG,EAAE,iBAAiB;iBACvB,CAAC,CAAC;gBAEH,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;aACnD;YAAC,OAAO,CAAC,EAAE;gBACV,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;gBAEjC,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;aACnD;;;;CACF;AAjBD,wCAiBC"}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import * as YAML from "js-yaml";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* If we want to add more built-in parsers,
|
|
5
|
-
* add them to this object with new file extension as the key,
|
|
6
|
-
* and a function that takes file content as string and returns parsed object as the value.
|
|
7
|
-
*/
|
|
8
|
-
export const parsers = {
|
|
9
|
-
// extension => function
|
|
10
|
-
yml(content: string) {
|
|
11
|
-
return YAML.load(content);
|
|
12
|
-
},
|
|
13
|
-
|
|
14
|
-
json(content: string) {
|
|
15
|
-
return JSON.parse(content);
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export type BuiltInParser = keyof typeof parsers; // keys of parsers object
|
|
20
|
-
|
|
21
|
-
export interface CustomParser {
|
|
22
|
-
extension: string;
|
|
23
|
-
parse: (content: string) => any;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export type Parser = BuiltInParser | CustomParser;
|
|
File without changes
|
|
File without changes
|