masterrecord 0.3.67 → 0.3.68
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 +72 -16
- package/Migrations/migrationTemplate.js +32 -1
- package/Migrations/migrations.js +2 -2
- package/package.json +1 -1
package/Migrations/cli.js
CHANGED
|
@@ -7,8 +7,60 @@
|
|
|
7
7
|
const { program } = require('commander');
|
|
8
8
|
let fs = require('fs');
|
|
9
9
|
let path = require('path');
|
|
10
|
+
const { pathToFileURL } = require('node:url');
|
|
10
11
|
const Module = require('module');
|
|
11
12
|
const { resolveMigrationsDirectory } = require('./pathUtils');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Load a user module (context, migration) via dynamic import.
|
|
16
|
+
*
|
|
17
|
+
* Handles both CJS and ESM targets. Required because:
|
|
18
|
+
* - CJS require() of an ESM file throws on older Node, or returns a Module
|
|
19
|
+
* namespace on newer Node (22.12+) — neither shape matches the
|
|
20
|
+
* `new ContextCtor()` pattern downstream.
|
|
21
|
+
* - await import() works in both directions and is consistent across Node
|
|
22
|
+
* versions.
|
|
23
|
+
*
|
|
24
|
+
* The returned value is unwrapped: ESM `export default X` -> X;
|
|
25
|
+
* CJS `module.exports = X` -> X; mixed shapes are handled via `.default ?? mod`.
|
|
26
|
+
*
|
|
27
|
+
* @param {string} filePath - Absolute path to the user file
|
|
28
|
+
* @returns {Promise<*>} The default export (or whole module if no default)
|
|
29
|
+
*/
|
|
30
|
+
async function __loadUserModule(filePath) {
|
|
31
|
+
const mod = await import(pathToFileURL(filePath).href);
|
|
32
|
+
return (mod && mod.default !== undefined) ? mod.default : mod;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Walk up from a given directory looking for the host project's package.json
|
|
37
|
+
* and return whether it declares ESM (`"type": "module"`) or CJS.
|
|
38
|
+
* Used when generating migration files so the emitted syntax matches the
|
|
39
|
+
* host project's module type. Masterrecord's own package.json is skipped.
|
|
40
|
+
*
|
|
41
|
+
* @param {string} startDir - Directory to walk up from
|
|
42
|
+
* @returns {'esm' | 'cjs'}
|
|
43
|
+
*/
|
|
44
|
+
function __detectHostModuleType(startDir) {
|
|
45
|
+
let dir = startDir;
|
|
46
|
+
for (let i = 0; i < 12; i++) {
|
|
47
|
+
const pkgPath = path.join(dir, 'package.json');
|
|
48
|
+
if (fs.existsSync(pkgPath)) {
|
|
49
|
+
try {
|
|
50
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
51
|
+
if (pkg && pkg.name === 'masterrecord') {
|
|
52
|
+
// Skip our own package.json — keep walking up to the host project
|
|
53
|
+
} else {
|
|
54
|
+
return (pkg && pkg.type === 'module') ? 'esm' : 'cjs';
|
|
55
|
+
}
|
|
56
|
+
} catch (_) { /* keep walking */ }
|
|
57
|
+
}
|
|
58
|
+
const parent = path.dirname(dir);
|
|
59
|
+
if (parent === dir) break;
|
|
60
|
+
dir = parent;
|
|
61
|
+
}
|
|
62
|
+
return 'cjs';
|
|
63
|
+
}
|
|
12
64
|
// Alias require('masterrecord') to this global package so project files don't need a local install
|
|
13
65
|
const __MASTERRECORD_ROOT__ = path.join(__dirname, '..');
|
|
14
66
|
const __ORIGINAL_REQUIRE__ = Module.prototype.require;
|
|
@@ -139,7 +191,7 @@ program.option('-V', 'output the version');
|
|
|
139
191
|
|
|
140
192
|
let ContextCtor;
|
|
141
193
|
try{
|
|
142
|
-
ContextCtor =
|
|
194
|
+
ContextCtor = await __loadUserModule(contextAbs);
|
|
143
195
|
}catch(err){
|
|
144
196
|
console.error(`\n❌ Error - Cannot load Context file at '${contextAbs}'`);
|
|
145
197
|
console.error(`\nDetails:`);
|
|
@@ -152,7 +204,7 @@ program.option('-V', 'output the version');
|
|
|
152
204
|
}
|
|
153
205
|
|
|
154
206
|
// Use the migration class (extends schema) so createdatabase is available
|
|
155
|
-
var MigrationCtor =
|
|
207
|
+
var MigrationCtor = await __loadUserModule(mFile);
|
|
156
208
|
var mig = new MigrationCtor(ContextCtor);
|
|
157
209
|
contextInstance = mig._context || mig.context || null;
|
|
158
210
|
|
|
@@ -256,7 +308,7 @@ program.option('-V', 'output the version');
|
|
|
256
308
|
|
|
257
309
|
let ContextCtor;
|
|
258
310
|
try{
|
|
259
|
-
ContextCtor =
|
|
311
|
+
ContextCtor = await __loadUserModule(contextAbs);
|
|
260
312
|
}catch(err){
|
|
261
313
|
console.error(`\n❌ Error - Cannot load Context file at '${contextAbs}'`);
|
|
262
314
|
console.error(`\nDetails:`);
|
|
@@ -298,7 +350,10 @@ program.option('-V', 'output the version');
|
|
|
298
350
|
return;
|
|
299
351
|
}
|
|
300
352
|
|
|
301
|
-
|
|
353
|
+
// Emit the migration file in whatever module format the host project uses,
|
|
354
|
+
// so the generated .js file parses correctly when loaded by update-database.
|
|
355
|
+
var moduleType = __detectHostModuleType(path.dirname(contextAbs));
|
|
356
|
+
var newEntity = migration.template(name, contextSnapshot.schema, cleanEntities, seedData, seedConfig, null, moduleType);
|
|
302
357
|
if(!fs.existsSync(migBase)){
|
|
303
358
|
try{ fs.mkdirSync(migBase, { recursive: true }); }catch(_){ /* ignore */ }
|
|
304
359
|
}
|
|
@@ -384,8 +439,8 @@ program.option('-V', 'output the version');
|
|
|
384
439
|
var migrationProjectFile;
|
|
385
440
|
var ContextCtor;
|
|
386
441
|
try{
|
|
387
|
-
migrationProjectFile =
|
|
388
|
-
ContextCtor =
|
|
442
|
+
migrationProjectFile = await __loadUserModule(mFile);
|
|
443
|
+
ContextCtor = await __loadUserModule(contextAbs);
|
|
389
444
|
}catch(err){
|
|
390
445
|
console.error(`\n❌ Error - Cannot load Context or migration file`);
|
|
391
446
|
console.error(`\nContext file: ${contextAbs}`);
|
|
@@ -547,7 +602,7 @@ program.option('-V', 'output the version');
|
|
|
547
602
|
// Prepare context and table object
|
|
548
603
|
let ContextCtor;
|
|
549
604
|
try{
|
|
550
|
-
ContextCtor =
|
|
605
|
+
ContextCtor = await __loadUserModule(contextAbs);
|
|
551
606
|
}catch(err){
|
|
552
607
|
console.error(`\n❌ Error - Cannot load Context file at '${contextAbs}'`);
|
|
553
608
|
console.error(`\nDetails:`);
|
|
@@ -578,7 +633,7 @@ program.option('-V', 'output the version');
|
|
|
578
633
|
var cleanEntities = migration.cleanEntities(contextInstance.__entities);
|
|
579
634
|
var tableObj = migration.buildUpObject(contextSnapshot.schema, cleanEntities);
|
|
580
635
|
|
|
581
|
-
var MigCtor =
|
|
636
|
+
var MigCtor = await __loadUserModule(latestFile);
|
|
582
637
|
var migInstance = new MigCtor(ContextCtor);
|
|
583
638
|
if(typeof migInstance.down === 'function'){
|
|
584
639
|
await migInstance.down(tableObj);
|
|
@@ -646,7 +701,7 @@ program.option('-V', 'output the version');
|
|
|
646
701
|
});
|
|
647
702
|
let ContextCtor;
|
|
648
703
|
try{
|
|
649
|
-
ContextCtor =
|
|
704
|
+
ContextCtor = await __loadUserModule(contextAbs);
|
|
650
705
|
}catch(err){
|
|
651
706
|
console.error(`\n❌ Error - Cannot load Context file at '${contextAbs}'`);
|
|
652
707
|
console.error(`\nDetails:`);
|
|
@@ -677,7 +732,7 @@ program.option('-V', 'output the version');
|
|
|
677
732
|
var cleanEntities = migration.cleanEntities(contextInstance.__entities);
|
|
678
733
|
for (let i = 0; i < mFiles.length; i++) {
|
|
679
734
|
var migFile = mFiles[i];
|
|
680
|
-
var migrationProjectFile =
|
|
735
|
+
var migrationProjectFile = await __loadUserModule(migFile);
|
|
681
736
|
var newMigrationProjectInstance = new migrationProjectFile(ContextCtor);
|
|
682
737
|
var tableObj = migration.buildUpObject(contextSnapshot.schema, cleanEntities);
|
|
683
738
|
await newMigrationProjectInstance.up(tableObj);
|
|
@@ -795,7 +850,7 @@ program.option('-V', 'output the version');
|
|
|
795
850
|
// Prepare context and table object
|
|
796
851
|
let ContextCtor;
|
|
797
852
|
try{
|
|
798
|
-
ContextCtor =
|
|
853
|
+
ContextCtor = await __loadUserModule(contextAbs);
|
|
799
854
|
}catch(err){
|
|
800
855
|
console.error(`\n❌ Error - Cannot load Context file at '${contextAbs}'`);
|
|
801
856
|
console.error(`\nDetails:`);
|
|
@@ -829,7 +884,7 @@ program.option('-V', 'output the version');
|
|
|
829
884
|
// Roll back (down) all migrations newer than the target (i.e., strictly after targetIndex)
|
|
830
885
|
for (var i = sorted.length - 1; i > targetIndex; i--) {
|
|
831
886
|
var migFile = path.resolve(migrationFolder, sorted[i]);
|
|
832
|
-
var MigCtor =
|
|
887
|
+
var MigCtor = await __loadUserModule(migFile);
|
|
833
888
|
var migInstance = new MigCtor(ContextCtor);
|
|
834
889
|
if(typeof migInstance.down === 'function'){
|
|
835
890
|
await migInstance.down(tableObj);
|
|
@@ -884,7 +939,7 @@ program.option('-V', 'output the version');
|
|
|
884
939
|
// Load context
|
|
885
940
|
let ContextCtor;
|
|
886
941
|
try{
|
|
887
|
-
ContextCtor =
|
|
942
|
+
ContextCtor = await __loadUserModule(contextAbs);
|
|
888
943
|
}catch(err){
|
|
889
944
|
console.error(`⚠️ Skipping ${path.basename(contextAbs)}: cannot load Context file`);
|
|
890
945
|
console.error(` Details: ${err.message}`);
|
|
@@ -909,7 +964,8 @@ program.option('-V', 'output the version');
|
|
|
909
964
|
console.log(`No changes detected for ${path.basename(contextAbs)}. Skipping.`);
|
|
910
965
|
continue;
|
|
911
966
|
}
|
|
912
|
-
var
|
|
967
|
+
var moduleType = __detectHostModuleType(path.dirname(contextAbs));
|
|
968
|
+
var newEntity = migration.template(name, cs.schema, cleanEntities, seedData, seedConfig, null, moduleType);
|
|
913
969
|
if(!fs.existsSync(migBase)){
|
|
914
970
|
try{ fs.mkdirSync(migBase, { recursive: true }); }catch(_){ /* ignore */ }
|
|
915
971
|
}
|
|
@@ -1001,7 +1057,7 @@ program.option('-V', 'output the version');
|
|
|
1001
1057
|
|
|
1002
1058
|
var ContextCtor;
|
|
1003
1059
|
try{
|
|
1004
|
-
ContextCtor =
|
|
1060
|
+
ContextCtor = await __loadUserModule(entry.contextAbs);
|
|
1005
1061
|
}catch(err){
|
|
1006
1062
|
console.error(`⚠️ Skipping ${entry.ctxName}: cannot load Context file`);
|
|
1007
1063
|
console.error(` Details: ${err.message}`);
|
|
@@ -1016,7 +1072,7 @@ program.option('-V', 'output the version');
|
|
|
1016
1072
|
console.error(` Details: ${err.message}`);
|
|
1017
1073
|
continue;
|
|
1018
1074
|
}
|
|
1019
|
-
var migrationProjectFile =
|
|
1075
|
+
var migrationProjectFile = await __loadUserModule(mFile);
|
|
1020
1076
|
var newMigrationProjectInstance = new migrationProjectFile(ContextCtor);
|
|
1021
1077
|
var cleanEntities = migration.cleanEntities(contextInstance.__entities);
|
|
1022
1078
|
var tableObj = migration.buildUpObject(entry.cs.schema, cleanEntities);
|
|
@@ -13,7 +13,38 @@ class MigrationTemplate {
|
|
|
13
13
|
#up = ''
|
|
14
14
|
#down = ''
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Render the migration file source.
|
|
18
|
+
*
|
|
19
|
+
* @param {'esm'|'cjs'} [moduleType='cjs'] - Module type of the host project.
|
|
20
|
+
* When 'esm' the file is emitted with ESM syntax so it can be loaded in
|
|
21
|
+
* a `"type": "module"` project. When 'cjs' (default) it's emitted with
|
|
22
|
+
* CJS syntax for backward compatibility with existing projects.
|
|
23
|
+
*/
|
|
24
|
+
get(moduleType = 'cjs'){
|
|
25
|
+
if (moduleType === 'esm') {
|
|
26
|
+
return `
|
|
27
|
+
import masterrecord from 'masterrecord';
|
|
28
|
+
|
|
29
|
+
class ${this.name} extends masterrecord.schema {
|
|
30
|
+
constructor(context){
|
|
31
|
+
super(context);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async up(table){
|
|
35
|
+
await this.init(table);
|
|
36
|
+
${this.#up}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async down(table){
|
|
40
|
+
await this.init(table);
|
|
41
|
+
${this.#down}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export default ${this.name};
|
|
45
|
+
`;
|
|
46
|
+
}
|
|
47
|
+
|
|
17
48
|
return `
|
|
18
49
|
|
|
19
50
|
var masterrecord = require('masterrecord');
|
package/Migrations/migrations.js
CHANGED
|
@@ -511,7 +511,7 @@ class Migrations{
|
|
|
511
511
|
return false;
|
|
512
512
|
}
|
|
513
513
|
|
|
514
|
-
template(name, oldSchema, newSchema, newSeedData = {}, seedConfig = {}, currentEnv = null){
|
|
514
|
+
template(name, oldSchema, newSchema, newSeedData = {}, seedConfig = {}, currentEnv = null, moduleType = 'cjs'){
|
|
515
515
|
var MT = new MigrationTemplate(name);
|
|
516
516
|
// Determine current environment if not provided
|
|
517
517
|
if (!currentEnv) {
|
|
@@ -578,7 +578,7 @@ class Migrations{
|
|
|
578
578
|
|
|
579
579
|
});
|
|
580
580
|
|
|
581
|
-
return MT.get();
|
|
581
|
+
return MT.get(moduleType);
|
|
582
582
|
}
|
|
583
583
|
|
|
584
584
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "masterrecord",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.68",
|
|
4
4
|
"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 ",
|
|
5
5
|
"main": "MasterRecord.js",
|
|
6
6
|
"bin": {
|