@cepharum/concrete-db 0.1.2-alpha.1 → 0.2.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/concrete-db.d.ts +37 -21
- package/cure.mjs +37 -4
- package/lib/collector.mjs +22 -1
- package/lib/cure.mjs +1 -1
- package/lib/default.shape.yaml +2 -0
- package/lib/generator.mjs +22 -3
- package/lib/helper.mjs +2 -0
- package/lib/shaper.mjs +355 -229
- package/lib/term-functions.mjs +27 -16
- package/package.json +2 -2
package/concrete-db.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* (c)
|
|
2
|
+
* (c) 2022 cepharum GmbH, Berlin, http://cepharum.de
|
|
3
3
|
*
|
|
4
4
|
* The MIT License (MIT)
|
|
5
5
|
*
|
|
@@ -32,6 +32,13 @@ declare module "concrete-db" {
|
|
|
32
32
|
* Selects base folder for writing files of resulting concrete-db.
|
|
33
33
|
*/
|
|
34
34
|
outputFolder?: string;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Lists custom functions to expose in simple terms used in shape
|
|
38
|
+
* processing. For security reasons, you cannot replace regular term
|
|
39
|
+
* functions this way by intention.
|
|
40
|
+
*/
|
|
41
|
+
library?: { [functionName: string]: () => any };
|
|
35
42
|
}
|
|
36
43
|
|
|
37
44
|
export interface CollectorOptions {
|
|
@@ -65,6 +72,11 @@ declare module "concrete-db" {
|
|
|
65
72
|
*/
|
|
66
73
|
records: Map<string, CollectedRecord[]>
|
|
67
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Exposes local shapes of processed folders in order of processing.
|
|
77
|
+
*/
|
|
78
|
+
localShapes: Shape[];
|
|
79
|
+
|
|
68
80
|
/**
|
|
69
81
|
* Collects sources from provided folders.
|
|
70
82
|
*
|
|
@@ -83,8 +95,9 @@ declare module "concrete-db" {
|
|
|
83
95
|
* with associated data to expose.
|
|
84
96
|
*
|
|
85
97
|
* @param records
|
|
98
|
+
* @param localShapes local shapes of folders records have been collected from in same order as folders processed
|
|
86
99
|
*/
|
|
87
|
-
transform( records: CollectedSources ): Promise<Database>;
|
|
100
|
+
transform( records: CollectedSources, localShapes: Shape[] ): Promise<Database>;
|
|
88
101
|
}
|
|
89
102
|
|
|
90
103
|
/**
|
|
@@ -147,17 +160,17 @@ declare module "concrete-db" {
|
|
|
147
160
|
/**
|
|
148
161
|
* Describes a model's item processed during shaping stage.
|
|
149
162
|
*/
|
|
150
|
-
export interface
|
|
163
|
+
export interface ModelItem {
|
|
151
164
|
/**
|
|
152
|
-
*
|
|
165
|
+
* Lists segments of relative path of file this item's originating
|
|
166
|
+
* record has been read from.
|
|
153
167
|
*/
|
|
154
|
-
$
|
|
168
|
+
$segments: string[];
|
|
155
169
|
|
|
156
170
|
/**
|
|
157
|
-
*
|
|
158
|
-
* from.
|
|
171
|
+
* Refers to record this item is based on.
|
|
159
172
|
*/
|
|
160
|
-
$
|
|
173
|
+
$original: CollectedRecord;
|
|
161
174
|
|
|
162
175
|
/**
|
|
163
176
|
* Exposes shape of item's model in context of its originating record.
|
|
@@ -174,12 +187,6 @@ declare module "concrete-db" {
|
|
|
174
187
|
*/
|
|
175
188
|
$id: string;
|
|
176
189
|
|
|
177
|
-
/**
|
|
178
|
-
* Lists segments of relative path of file this item's originating
|
|
179
|
-
* record has been read from.
|
|
180
|
-
*/
|
|
181
|
-
$segments: string[];
|
|
182
|
-
|
|
183
190
|
/**
|
|
184
191
|
* item's properties
|
|
185
192
|
*/
|
|
@@ -198,6 +205,22 @@ declare module "concrete-db" {
|
|
|
198
205
|
* of model or an such an item's representation in a collection).
|
|
199
206
|
*/
|
|
200
207
|
export interface ShapeProperties {
|
|
208
|
+
/**
|
|
209
|
+
* Provides term computed to get the name of a record's model.
|
|
210
|
+
*/
|
|
211
|
+
$model?: string;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Provides term computed to get ID of item described by a record.
|
|
215
|
+
*/
|
|
216
|
+
$id?: string;
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Provides term computed to get ID of selected item's variant described
|
|
220
|
+
* by record.
|
|
221
|
+
*/
|
|
222
|
+
$variant?: string;
|
|
223
|
+
|
|
201
224
|
[propertyName: string]: any;
|
|
202
225
|
}
|
|
203
226
|
|
|
@@ -249,13 +272,6 @@ declare module "concrete-db" {
|
|
|
249
272
|
[targetPropertyName: string]: string;
|
|
250
273
|
};
|
|
251
274
|
};
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Declares term to compute an item's variant. If falsy or omitted, the
|
|
255
|
-
* model does not have variants and multiple records describing an item
|
|
256
|
-
* are merged property by property.
|
|
257
|
-
*/
|
|
258
|
-
variant?: string;
|
|
259
275
|
}
|
|
260
276
|
|
|
261
277
|
export interface Shape {
|
package/cure.mjs
CHANGED
|
@@ -22,10 +22,15 @@ Processes current folder by default.
|
|
|
22
22
|
|
|
23
23
|
Supported options are:
|
|
24
24
|
|
|
25
|
-
-h / --help
|
|
26
|
-
-V / --version
|
|
27
|
-
-
|
|
28
|
-
|
|
25
|
+
-h / --help showing this information
|
|
26
|
+
-V / --version showing version of this package
|
|
27
|
+
-v / --verbose be verbose about the progress of curing database
|
|
28
|
+
--debug be extra verbose
|
|
29
|
+
-s / --shape=file selects global shape controlling transformation
|
|
30
|
+
-o / --output=folder selects folder resulting files are written to
|
|
31
|
+
-c / --clean remove all existing files from output folder
|
|
32
|
+
-l / --library=file selects file exposing custom term functions to use
|
|
33
|
+
in addition to regular ones
|
|
29
34
|
` );
|
|
30
35
|
process.exit( 0 );
|
|
31
36
|
}
|
|
@@ -40,10 +45,38 @@ if ( args.V || args.version ) {
|
|
|
40
45
|
( async() => {
|
|
41
46
|
const options = {
|
|
42
47
|
outputFolder: args.o || args.output || process.cwd(),
|
|
48
|
+
verbose: args.debug ? 2 : args.v || args.verbose ? 1 : 0,
|
|
49
|
+
clean: Boolean( args.c || args.clean ),
|
|
43
50
|
};
|
|
44
51
|
|
|
45
52
|
const globalShapeFile = args.s || args.shape || Path.resolve( process.cwd(), ShapeFileName );
|
|
46
53
|
|
|
54
|
+
const libraries = Array.isArray( args.l ) ? args.l : args.l ? [args.l] : [];
|
|
55
|
+
libraries.splice( libraries.length, 0, ...Array.isArray( args.library ) ? args.library : args.library ? [args.library] : [] );
|
|
56
|
+
|
|
57
|
+
options.library = {};
|
|
58
|
+
for ( const library of libraries ) {
|
|
59
|
+
let pathname = Path.resolve( ".", library );
|
|
60
|
+
|
|
61
|
+
if ( options.verbose ) {
|
|
62
|
+
console.error( `including library ${pathname}` );
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if ( /^[a-z]:/i.test( pathname ) ) {
|
|
66
|
+
pathname = "file://" + pathname.replace( /\//g, "%2F" );
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const functions = await import( pathname ); // eslint-disable-line no-await-in-loop
|
|
70
|
+
|
|
71
|
+
for ( const name of Object.keys( functions ) ) {
|
|
72
|
+
if ( options.verbose > 1 ) {
|
|
73
|
+
console.error( ` - ${name}` );
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
options.library[name] = functions[name];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
47
80
|
try {
|
|
48
81
|
options.shape = Object.freeze( YAML.parse( await File.promises.readFile( globalShapeFile, { encoding: "utf8" } ), YAMLParserOptions ) );
|
|
49
82
|
} catch ( cause ) {
|
package/lib/collector.mjs
CHANGED
|
@@ -35,6 +35,7 @@ export class Collector extends EventEmitter {
|
|
|
35
35
|
enumerable: true
|
|
36
36
|
},
|
|
37
37
|
records: { value: new Map(), enumerable: true },
|
|
38
|
+
localShapes: { value: [], enumerable: true },
|
|
38
39
|
} );
|
|
39
40
|
}
|
|
40
41
|
|
|
@@ -72,10 +73,19 @@ export class Collector extends EventEmitter {
|
|
|
72
73
|
* @returns {Promise<Collector>} promises collector having collected records from folders
|
|
73
74
|
*/
|
|
74
75
|
async fromFolders( folders ) {
|
|
76
|
+
const spinner = "⠁⠂⠄⡀⢀⠠⠐⠈";
|
|
75
77
|
const { pattern, nameToKey } = this.options;
|
|
76
78
|
|
|
77
|
-
await PromiseEssentials.each( folders || [], async folder => {
|
|
79
|
+
await PromiseEssentials.each( folders || [], async( folder, index ) => {
|
|
80
|
+
if ( this.options.verbose ) {
|
|
81
|
+
process.stderr.write( `collecting records from folder ${folder} [${index + 1}/${folders.length}] ... ` );
|
|
82
|
+
}
|
|
83
|
+
|
|
78
84
|
const localShape = await this.getLocalShape( folder );
|
|
85
|
+
let total = 0;
|
|
86
|
+
let additional = 0;
|
|
87
|
+
|
|
88
|
+
this.localShapes.push( localShape );
|
|
79
89
|
|
|
80
90
|
await FileEssentials.find( folder, {
|
|
81
91
|
filter: ( localPath, fullPath, stat ) => {
|
|
@@ -105,15 +115,26 @@ export class Collector extends EventEmitter {
|
|
|
105
115
|
|
|
106
116
|
if ( this.records.has( name ) ) {
|
|
107
117
|
this.records.get( name ).push( record );
|
|
118
|
+
additional++;
|
|
108
119
|
} else {
|
|
109
120
|
this.records.set( name, [record] );
|
|
110
121
|
}
|
|
122
|
+
|
|
123
|
+
total++;
|
|
124
|
+
|
|
125
|
+
if ( this.options.verbose ) {
|
|
126
|
+
process.stderr.write( `${spinner[Math.floor( total / 10 ) % spinner.length]}\x08` );
|
|
127
|
+
}
|
|
111
128
|
}
|
|
112
129
|
|
|
113
130
|
return undefined;
|
|
114
131
|
},
|
|
115
132
|
waitForConverter: true,
|
|
116
133
|
} );
|
|
134
|
+
|
|
135
|
+
if ( this.options.verbose ) {
|
|
136
|
+
process.stderr.write( `${total} records (${additional} overlays)\n` );
|
|
137
|
+
}
|
|
117
138
|
} );
|
|
118
139
|
|
|
119
140
|
return this;
|
package/lib/cure.mjs
CHANGED
|
@@ -31,6 +31,6 @@ export async function cure( folders, options = {}, stages = {} ) {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
const collector = await _stages.collector.fromFolders( sources );
|
|
34
|
-
const database = await _stages.shaper.transform( collector.records );
|
|
34
|
+
const database = await _stages.shaper.transform( collector.records, collector.localShapes );
|
|
35
35
|
await _stages.generator.writeDatabase( database );
|
|
36
36
|
}
|
package/lib/default.shape.yaml
CHANGED
package/lib/generator.mjs
CHANGED
|
@@ -40,6 +40,10 @@ export class Generator extends EventEmitter {
|
|
|
40
40
|
await FileEssentials.mkdir( "/", subfolder );
|
|
41
41
|
|
|
42
42
|
if ( this.options.clean ) {
|
|
43
|
+
if ( this.options.verbose ) {
|
|
44
|
+
console.error( `cleaning output folder ${subfolder} ...` );
|
|
45
|
+
}
|
|
46
|
+
|
|
43
47
|
await FileEssentials.find( subfolder, {
|
|
44
48
|
depthFirst: true,
|
|
45
49
|
filter: ( localPath, fullPath, stat ) => {
|
|
@@ -49,11 +53,11 @@ export class Generator extends EventEmitter {
|
|
|
49
53
|
|
|
50
54
|
return stat.isFile();
|
|
51
55
|
},
|
|
52
|
-
converter: ( localPath, fullPath ) => {
|
|
56
|
+
converter: ( localPath, fullPath, stat ) => {
|
|
53
57
|
const name = fullPath.replace( /\.json$/, "" );
|
|
54
58
|
|
|
55
59
|
if ( !database.hasOwnProperty( name ) ) {
|
|
56
|
-
return File.promises.unlink( fullPath );
|
|
60
|
+
return stat.isDirectory() ? File.promises.rmdir( fullPath ) : File.promises.unlink( fullPath );
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
return undefined;
|
|
@@ -62,14 +66,29 @@ export class Generator extends EventEmitter {
|
|
|
62
66
|
} );
|
|
63
67
|
}
|
|
64
68
|
|
|
65
|
-
|
|
69
|
+
const keys = Object.keys( endpoints );
|
|
70
|
+
let index = 0;
|
|
71
|
+
|
|
72
|
+
if ( this.options.verbose ) {
|
|
73
|
+
console.error( `writing ${keys.length} files` );
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
await PromiseEssentials.each( keys, key => {
|
|
66
77
|
const name = key.endsWith( ".json" ) ? key : key + ".json";
|
|
67
78
|
const path = name.indexOf( "/" ) > -1 ? name.replace( /\/[^/]*?$/, "" ) : undefined;
|
|
68
79
|
|
|
80
|
+
if ( this.options.verbose ) {
|
|
81
|
+
process.stderr.write( `\r[${++index}/${keys.length}]` );
|
|
82
|
+
}
|
|
83
|
+
|
|
69
84
|
return FileEssentials.mkdir( folder, path )
|
|
70
85
|
.then( () => File.promises.writeFile( Path.resolve( folder, name ), JSON.stringify( endpoints[key] ), { encoding: "utf8" } ) );
|
|
71
86
|
} );
|
|
72
87
|
|
|
88
|
+
if ( this.options.verbose ) {
|
|
89
|
+
process.stderr.write( "\n" );
|
|
90
|
+
}
|
|
91
|
+
|
|
73
92
|
return this;
|
|
74
93
|
}
|
|
75
94
|
}
|