masterrecord 0.2.20 → 0.2.22

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/Migrations/cli.js CHANGED
@@ -191,24 +191,40 @@ program.option('-V', 'output the version');
191
191
  return;
192
192
  }
193
193
 
194
+ // Resolve relative paths from the snapshot directory (portable snapshots)
195
+ const snapDir = path.dirname(file);
196
+ const contextAbs = path.resolve(snapDir, contextSnapshot.contextLocation || '');
197
+ const migBase = path.resolve(snapDir, contextSnapshot.migrationFolder || '.');
198
+
194
199
  let ContextCtor;
195
200
  try{
196
- ContextCtor = require(contextSnapshot.contextLocation);
201
+ ContextCtor = require(contextAbs);
197
202
  }catch(_){
198
- console.log(`Error - Cannot load Context file at '${contextSnapshot.contextLocation}'.`);
203
+ console.log(`Error - Cannot load Context file at '${contextAbs}'.`);
199
204
  return;
200
205
  }
201
206
  let contextInstance;
202
207
  try{
203
208
  contextInstance = new ContextCtor();
204
209
  }catch(_){
205
- console.log(`Error - Failed to construct Context from '${contextSnapshot.contextLocation}'.`);
210
+ console.log(`Error - Failed to construct Context from '${contextAbs}'.`);
206
211
  return;
207
212
  }
208
213
  var cleanEntities = migration.cleanEntities(contextInstance.__entities);
214
+
215
+ // Skip if no changes between snapshot schema and current entities
216
+ const has = migration.hasChanges(contextSnapshot.schema || [], cleanEntities || []);
217
+ if(!has){
218
+ console.log(`No changes detected for ${path.basename(contextAbs)}. Skipping.`);
219
+ return;
220
+ }
221
+
209
222
  var newEntity = migration.template(name, contextSnapshot.schema, cleanEntities);
223
+ if(!fs.existsSync(migBase)){
224
+ try{ fs.mkdirSync(migBase, { recursive: true }); }catch(_){ /* ignore */ }
225
+ }
210
226
  var migrationDate = Date.now();
211
- var outputFile = `${contextSnapshot.migrationFolder}/${migrationDate}_${name}_migration.js`
227
+ var outputFile = `${migBase}/${migrationDate}_${name}_migration.js`
212
228
  fs.writeFile(outputFile, newEntity, 'utf8', function (err) {
213
229
  if (err) return console.log("--- Error running cammand, re-run command add-migration ---- ", err);
214
230
  });
@@ -595,6 +611,12 @@ program.option('-V', 'output the version');
595
611
  }
596
612
  var migration = new Migration();
597
613
  var cleanEntities = migration.cleanEntities(contextInstance.__entities);
614
+ // If no changes, skip with message
615
+ const has = migration.hasChanges(cs.schema || [], cleanEntities || []);
616
+ if(!has){
617
+ console.log(`No changes detected for ${path.basename(contextAbs)}. Skipping.`);
618
+ continue;
619
+ }
598
620
  var newEntity = migration.template(name, cs.schema, cleanEntities);
599
621
  if(!fs.existsSync(migBase)){
600
622
  try{ fs.mkdirSync(migBase, { recursive: true }); }catch(_){ /* ignore */ }
@@ -277,6 +277,22 @@ class Migrations{
277
277
  return tableObj;
278
278
  }
279
279
 
280
+ // Returns true if there are any changes between old and new schema
281
+ hasChanges(oldSchema, newSchema){
282
+ const tables = this.#buildMigrationObject(oldSchema, newSchema);
283
+ for(const t of tables){
284
+ if(!t) continue;
285
+ if((t.newTables && t.newTables.length) ||
286
+ (t.newColumns && t.newColumns.length) ||
287
+ (t.deletedColumns && t.deletedColumns.length) ||
288
+ (t.updatedColumns && t.updatedColumns.length) ||
289
+ (t.old === null) || (t.new === null)){
290
+ return true;
291
+ }
292
+ }
293
+ return false;
294
+ }
295
+
280
296
  template(name, oldSchema, newSchema){
281
297
  var MT = new MigrationTemplate(name);
282
298
  var tables = this.#buildMigrationObject(oldSchema, newSchema);
package/context.js CHANGED
@@ -98,17 +98,19 @@ class context {
98
98
  let currentRoot = root;
99
99
  const maxHops = 12;
100
100
  for(let i = 0; i < maxHops; i++){
101
- const rootFolder = path.isAbsolute(rootFolderLocation) ? rootFolderLocation : `${currentRoot}/${rootFolderLocation}`;
101
+ const rootFolder = path.isAbsolute(rootFolderLocation) ? rootFolderLocation : path.join(currentRoot, rootFolderLocation);
102
102
  // Support both env.development.json and development.json naming
103
103
  const searchA = `${rootFolder}/**/*env.${envType}.json`;
104
104
  const searchB = `${rootFolder}/**/*${envType}.json`;
105
- let files = globSearch.sync(searchA, currentRoot);
105
+ let files = globSearch.sync(searchA, { cwd: currentRoot, dot: true, nocase: true, windowsPathsNoEscape: true });
106
106
  if(!files || files.length === 0){
107
- files = globSearch.sync(searchB, currentRoot);
107
+ files = globSearch.sync(searchB, { cwd: currentRoot, dot: true, nocase: true, windowsPathsNoEscape: true });
108
108
  }
109
- const file = files && files[0];
110
- if(file){
111
- return { file: file, rootFolder: currentRoot };
109
+ const rel = files && files[0];
110
+ if(rel){
111
+ // Ensure absolute path for require()
112
+ const abs = path.isAbsolute(rel) ? rel : path.resolve(currentRoot, rel);
113
+ return { file: abs, rootFolder: currentRoot };
112
114
  }
113
115
  const parent = tools.removeBackwardSlashSection(currentRoot, 1, "/");
114
116
  if(parent === currentRoot || parent === ""){
@@ -151,7 +153,9 @@ class context {
151
153
  throw new Error(`Environment config not found for '${envType}' under '${rootFolderLocation}'.`);
152
154
  }
153
155
 
154
- const settings = require(file.file);
156
+ // Always require absolute file path to avoid module root ambiguity on global installs/Windows
157
+ const settingsPath = path.isAbsolute(file.file) ? file.file : path.resolve(file.rootFolder, file.file);
158
+ const settings = require(settingsPath);
155
159
  const options = settings[contextName];
156
160
  if(options === undefined){
157
161
  console.log("settings missing context name settings");
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "app-root-path": "^3.1.0",
10
10
  "better-sqlite3": "^12.4.1"
11
11
  },
12
- "version": "0.2.20",
12
+ "version": "0.2.22",
13
13
  "description": "An Object-relational mapping for the Master framework. Master Record connects classes to relational database tables to establish a database with almost zero-configuration ",
14
14
  "homepage": "https://github.com/Tailor/MasterRecord#readme",
15
15
  "repository": {