@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 CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * (c) 2021 cepharum GmbH, Berlin, http://cepharum.de
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 CollectedItem {
163
+ export interface ModelItem {
151
164
  /**
152
- * Refers to record this item is based on.
165
+ * Lists segments of relative path of file this item's originating
166
+ * record has been read from.
153
167
  */
154
- $original: CollectedRecord;
168
+ $segments: string[];
155
169
 
156
170
  /**
157
- * Exposes local shape declared in context of record item was collected
158
- * from.
171
+ * Refers to record this item is based on.
159
172
  */
160
- $localShape: ShapeModel;
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 showing this information
26
- -V / --version showing version of this package
27
- -s / --shape=file selects global shape controlling transformation
28
- -o / --output=folder selects folder resulting files are written to
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
  }
@@ -1,6 +1,8 @@
1
1
  root: true
2
2
 
3
3
  common:
4
+ defaults: {}
5
+
4
6
  properties:
5
7
  $model: model
6
8
  $id: id
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
- await PromiseEssentials.each( Object.keys( endpoints ), key => {
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
  }
package/lib/helper.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ export * from "simple-terms/lib/helper.js";
2
+
1
3
  /**
2
4
  * Wraps provided object in a proxy offering case-insensitive access on
3
5
  * object's properties. Any accessed property's value is wrapped before