@ackplus/nest-seeder 1.1.17 → 2.0.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/CHANGELOG.md +64 -0
- package/MIGRATION.md +89 -0
- package/QUICKSTART.md +45 -149
- package/README.md +87 -521
- package/dist/cli.js +595 -235
- package/dist/cli.js.map +1 -1
- package/dist/index.d.mts +88 -0
- package/dist/index.d.ts +88 -8
- package/dist/index.js +417 -24
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +390 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +46 -25
- package/dist/cli.d.ts +0 -3
- package/dist/lib/decorators/factory.decorator.d.ts +0 -6
- package/dist/lib/decorators/factory.decorator.js +0 -17
- package/dist/lib/decorators/factory.decorator.js.map +0 -1
- package/dist/lib/factory/data.factory.d.ts +0 -6
- package/dist/lib/factory/data.factory.js +0 -57
- package/dist/lib/factory/data.factory.js.map +0 -1
- package/dist/lib/index.d.ts +0 -4
- package/dist/lib/index.js +0 -21
- package/dist/lib/index.js.map +0 -1
- package/dist/lib/interfaces/factory.interface.d.ts +0 -4
- package/dist/lib/interfaces/factory.interface.js +0 -3
- package/dist/lib/interfaces/factory.interface.js.map +0 -1
- package/dist/lib/interfaces/index.d.ts +0 -2
- package/dist/lib/interfaces/index.js +0 -19
- package/dist/lib/interfaces/index.js.map +0 -1
- package/dist/lib/interfaces/property-metadata.interface.d.ts +0 -6
- package/dist/lib/interfaces/property-metadata.interface.js +0 -3
- package/dist/lib/interfaces/property-metadata.interface.js.map +0 -1
- package/dist/lib/interfaces/seeder-module-async-options.interface.d.ts +0 -10
- package/dist/lib/interfaces/seeder-module-async-options.interface.js +0 -3
- package/dist/lib/interfaces/seeder-module-async-options.interface.js.map +0 -1
- package/dist/lib/interfaces/seeder-options-factory.interface.d.ts +0 -4
- package/dist/lib/interfaces/seeder-options-factory.interface.js +0 -3
- package/dist/lib/interfaces/seeder-options-factory.interface.js.map +0 -1
- package/dist/lib/seeder/seeder.d.ts +0 -10
- package/dist/lib/seeder/seeder.interface.d.ts +0 -9
- package/dist/lib/seeder/seeder.interface.js +0 -3
- package/dist/lib/seeder/seeder.interface.js.map +0 -1
- package/dist/lib/seeder/seeder.js +0 -40
- package/dist/lib/seeder/seeder.js.map +0 -1
- package/dist/lib/seeder/seeder.module.d.ts +0 -17
- package/dist/lib/seeder/seeder.module.js +0 -80
- package/dist/lib/seeder/seeder.module.js.map +0 -1
- package/dist/lib/seeder/seeder.service.d.ts +0 -10
- package/dist/lib/seeder/seeder.service.js +0 -73
- package/dist/lib/seeder/seeder.service.js.map +0 -1
- package/dist/lib/storages/factory.metadata.storage.d.ts +0 -16
- package/dist/lib/storages/factory.metadata.storage.js +0 -17
- package/dist/lib/storages/factory.metadata.storage.js.map +0 -1
- package/dist/tsconfig.build.tsbuildinfo +0 -1
- package/examples/post.seeder.example.ts +0 -65
- package/examples/product.factory.example.ts +0 -84
- package/examples/seed.script.example.ts +0 -32
- package/examples/seeder.config.example.ts +0 -50
- package/examples/user.factory.example.ts +0 -59
- package/examples/user.seeder.example.ts +0 -55
package/dist/cli.js
CHANGED
|
@@ -1,256 +1,616 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
-
}) : function(o, v) {
|
|
17
|
-
o["default"] = v;
|
|
18
|
-
});
|
|
19
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
-
var ownKeys = function(o) {
|
|
21
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
-
var ar = [];
|
|
23
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
-
return ar;
|
|
25
|
-
};
|
|
26
|
-
return ownKeys(o);
|
|
27
|
-
};
|
|
28
|
-
return function (mod) {
|
|
29
|
-
if (mod && mod.__esModule) return mod;
|
|
30
|
-
var result = {};
|
|
31
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
-
__setModuleDefault(result, mod);
|
|
33
|
-
return result;
|
|
34
|
-
};
|
|
35
|
-
})();
|
|
36
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
38
15
|
};
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
alias: 'c',
|
|
69
|
-
type: 'string',
|
|
70
|
-
description: 'Path to seeder configuration file',
|
|
71
|
-
demandOption: true
|
|
72
|
-
})
|
|
73
|
-
.help()
|
|
74
|
-
.example('nest-seed -c ./seeder.config.ts', 'Run all seeders')
|
|
75
|
-
.example('nest-seed -c ./seeder.config.ts --refresh', 'Drop and reseed all data')
|
|
76
|
-
.example('nest-seed -c ./seeder.config.ts --name UserSeeder', 'Run specific seeder')
|
|
77
|
-
.parseAsync();
|
|
78
|
-
return {
|
|
79
|
-
refresh: argv.refresh,
|
|
80
|
-
name: argv.name,
|
|
81
|
-
dummyData: argv.dummyData,
|
|
82
|
-
config: argv.config,
|
|
83
|
-
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
25
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
26
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
27
|
+
if (decorator = decorators[i])
|
|
28
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
29
|
+
if (kind && result) __defProp(target, key, result);
|
|
30
|
+
return result;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// src/cli.ts
|
|
34
|
+
var fs2 = __toESM(require("fs"));
|
|
35
|
+
var path2 = __toESM(require("path"));
|
|
36
|
+
var import_core = require("@nestjs/core");
|
|
37
|
+
var import_yargs = __toESM(require("yargs"));
|
|
38
|
+
var import_helpers = require("yargs/helpers");
|
|
39
|
+
|
|
40
|
+
// src/lib/decorators/seeder.decorator.ts
|
|
41
|
+
var SEEDER_NAME = /* @__PURE__ */ Symbol.for("nest-seeder:name");
|
|
42
|
+
function getSeederName(seeder) {
|
|
43
|
+
const ctor = typeof seeder === "function" ? seeder : seeder?.constructor;
|
|
44
|
+
return ctor?.[SEEDER_NAME] || ctor?.seederName || ctor?.name || "UnknownSeeder";
|
|
84
45
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
46
|
+
|
|
47
|
+
// src/lib/cli/load-config.ts
|
|
48
|
+
var fs = __toESM(require("fs"));
|
|
49
|
+
var path = __toESM(require("path"));
|
|
50
|
+
var DEFAULT_CONFIG_NAMES = [
|
|
51
|
+
"seeder.config.ts",
|
|
52
|
+
"seeder.config.js",
|
|
53
|
+
"seeder.config.cjs",
|
|
54
|
+
"seeder.config.mjs"
|
|
55
|
+
];
|
|
56
|
+
function resolveConfigPath(configPath) {
|
|
57
|
+
if (configPath) {
|
|
58
|
+
const resolved = path.resolve(process.cwd(), configPath);
|
|
59
|
+
return fs.existsSync(resolved) ? resolved : null;
|
|
60
|
+
}
|
|
61
|
+
for (const name of DEFAULT_CONFIG_NAMES) {
|
|
62
|
+
const candidate = path.resolve(process.cwd(), name);
|
|
63
|
+
if (fs.existsSync(candidate)) {
|
|
64
|
+
return candidate;
|
|
88
65
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
var tsNodeRegistered = false;
|
|
70
|
+
function setupTsNode() {
|
|
71
|
+
if (tsNodeRegistered || require.extensions[".ts"]) {
|
|
72
|
+
tsNodeRegistered = true;
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
let tsNodePath;
|
|
76
|
+
try {
|
|
77
|
+
tsNodePath = require.resolve("ts-node", {
|
|
78
|
+
paths: [process.cwd(), __dirname]
|
|
79
|
+
});
|
|
80
|
+
} catch {
|
|
81
|
+
throw new Error(
|
|
82
|
+
'TypeScript config files require "ts-node" and "typescript" to be installed.\n Install them with: npm install -D ts-node typescript\n Or use a JavaScript config file (seeder.config.js).'
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
const tsNode = require(tsNodePath);
|
|
86
|
+
tsNode.register({
|
|
87
|
+
transpileOnly: true,
|
|
88
|
+
skipProject: true,
|
|
89
|
+
compilerOptions: {
|
|
90
|
+
module: "commonjs",
|
|
91
|
+
moduleResolution: "node",
|
|
92
|
+
experimentalDecorators: true,
|
|
93
|
+
emitDecoratorMetadata: true,
|
|
94
|
+
esModuleInterop: true,
|
|
95
|
+
allowSyntheticDefaultImports: true,
|
|
96
|
+
skipLibCheck: true,
|
|
97
|
+
target: "ES2021",
|
|
98
|
+
resolveJsonModule: true,
|
|
99
|
+
strict: false
|
|
110
100
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
101
|
+
});
|
|
102
|
+
tsNodeRegistered = true;
|
|
103
|
+
}
|
|
104
|
+
async function loadSeederConfig(absolutePath) {
|
|
105
|
+
const ext = path.extname(absolutePath);
|
|
106
|
+
if (ext === ".ts") {
|
|
107
|
+
setupTsNode();
|
|
108
|
+
}
|
|
109
|
+
let configModule;
|
|
110
|
+
try {
|
|
111
|
+
if (ext === ".mjs") {
|
|
112
|
+
configModule = await import(absolutePath);
|
|
113
|
+
} else {
|
|
114
|
+
delete require.cache[absolutePath];
|
|
115
|
+
configModule = require(absolutePath);
|
|
121
116
|
}
|
|
117
|
+
} catch (error) {
|
|
118
|
+
if (ext === ".ts" && (error?.code === "ERR_REQUIRE_ESM" || error?.code === "ERR_UNKNOWN_FILE_EXTENSION")) {
|
|
119
|
+
configModule = await import(absolutePath);
|
|
120
|
+
} else {
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const config = configModule?.default ?? configModule;
|
|
125
|
+
if (!config || typeof config !== "object") {
|
|
126
|
+
throw new Error(
|
|
127
|
+
"The config file must export a configuration object as its default export."
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
return config;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/lib/cli/templates.ts
|
|
134
|
+
var FACTORY = `import { Factory } from '@ackplus/nest-seeder';
|
|
135
|
+
|
|
136
|
+
export class UserFactory {
|
|
137
|
+
@Factory((faker) => faker.person.firstName())
|
|
138
|
+
firstName: string;
|
|
139
|
+
|
|
140
|
+
@Factory((faker) => faker.person.lastName())
|
|
141
|
+
lastName: string;
|
|
142
|
+
|
|
143
|
+
@Factory((faker, ctx) => faker.internet.email({ firstName: ctx.firstName, lastName: ctx.lastName }).toLowerCase(), ['firstName', 'lastName'])
|
|
144
|
+
email: string;
|
|
145
|
+
|
|
146
|
+
@Factory((faker) => faker.helpers.arrayElement(['admin', 'user', 'guest']))
|
|
147
|
+
role: string;
|
|
148
|
+
}
|
|
149
|
+
`;
|
|
150
|
+
var SEEDER_TYPEORM = `import { Injectable } from '@nestjs/common';
|
|
151
|
+
import { InjectRepository } from '@nestjs/typeorm';
|
|
152
|
+
import { Repository } from 'typeorm';
|
|
153
|
+
import { Seeder, SeederName, DataFactory } from '@ackplus/nest-seeder';
|
|
154
|
+
|
|
155
|
+
import { User } from '../entities/user.entity';
|
|
156
|
+
import { UserFactory } from '../factories/user.factory';
|
|
157
|
+
|
|
158
|
+
@Injectable()
|
|
159
|
+
@SeederName('users')
|
|
160
|
+
export class UserSeeder implements Seeder {
|
|
161
|
+
constructor(
|
|
162
|
+
@InjectRepository(User)
|
|
163
|
+
private readonly userRepository: Repository<User>,
|
|
164
|
+
) {}
|
|
165
|
+
|
|
166
|
+
async seed(): Promise<void> {
|
|
167
|
+
const factory = DataFactory.createForClass(UserFactory);
|
|
168
|
+
const users = factory.generate(10);
|
|
169
|
+
await this.userRepository.save(users);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async drop(): Promise<void> {
|
|
173
|
+
await this.userRepository.delete({});
|
|
174
|
+
}
|
|
122
175
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
176
|
+
`;
|
|
177
|
+
var SEEDER_MONGOOSE = `import { Injectable } from '@nestjs/common';
|
|
178
|
+
import { InjectModel } from '@nestjs/mongoose';
|
|
179
|
+
import { Model } from 'mongoose';
|
|
180
|
+
import { Seeder, SeederName, DataFactory } from '@ackplus/nest-seeder';
|
|
181
|
+
|
|
182
|
+
import { User } from '../schemas/user.schema';
|
|
183
|
+
import { UserFactory } from '../factories/user.factory';
|
|
184
|
+
|
|
185
|
+
@Injectable()
|
|
186
|
+
@SeederName('users')
|
|
187
|
+
export class UserSeeder implements Seeder {
|
|
188
|
+
constructor(
|
|
189
|
+
@InjectModel(User.name)
|
|
190
|
+
private readonly userModel: Model<User>,
|
|
191
|
+
) {}
|
|
192
|
+
|
|
193
|
+
async seed(): Promise<void> {
|
|
194
|
+
const factory = DataFactory.createForClass(UserFactory);
|
|
195
|
+
const users = factory.generate(10);
|
|
196
|
+
await this.userModel.insertMany(users);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async drop(): Promise<void> {
|
|
200
|
+
await this.userModel.deleteMany({});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
`;
|
|
204
|
+
var CONFIG_TYPEORM = `import { TypeOrmModule } from '@nestjs/typeorm';
|
|
205
|
+
import { defineSeederConfig } from '@ackplus/nest-seeder';
|
|
206
|
+
|
|
207
|
+
import { User } from './src/database/entities/user.entity';
|
|
208
|
+
import { UserSeeder } from './src/database/seeders/user.seeder';
|
|
209
|
+
|
|
210
|
+
export default defineSeederConfig({
|
|
211
|
+
imports: [
|
|
212
|
+
TypeOrmModule.forRoot({
|
|
213
|
+
type: 'postgres',
|
|
214
|
+
host: process.env.DB_HOST ?? 'localhost',
|
|
215
|
+
port: Number(process.env.DB_PORT ?? 5432),
|
|
216
|
+
username: process.env.DB_USERNAME ?? 'postgres',
|
|
217
|
+
password: process.env.DB_PASSWORD ?? 'postgres',
|
|
218
|
+
database: process.env.DB_DATABASE ?? 'app',
|
|
219
|
+
entities: [User],
|
|
220
|
+
synchronize: true,
|
|
221
|
+
}),
|
|
222
|
+
TypeOrmModule.forFeature([User]),
|
|
223
|
+
],
|
|
224
|
+
seeders: [UserSeeder],
|
|
225
|
+
});
|
|
226
|
+
`;
|
|
227
|
+
var CONFIG_MONGOOSE = `import { MongooseModule } from '@nestjs/mongoose';
|
|
228
|
+
import { defineSeederConfig } from '@ackplus/nest-seeder';
|
|
229
|
+
|
|
230
|
+
import { User, UserSchema } from './src/database/schemas/user.schema';
|
|
231
|
+
import { UserSeeder } from './src/database/seeders/user.seeder';
|
|
232
|
+
|
|
233
|
+
export default defineSeederConfig({
|
|
234
|
+
imports: [
|
|
235
|
+
MongooseModule.forRoot(process.env.MONGO_URI ?? 'mongodb://localhost/app'),
|
|
236
|
+
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
|
|
237
|
+
],
|
|
238
|
+
seeders: [UserSeeder],
|
|
239
|
+
});
|
|
240
|
+
`;
|
|
241
|
+
function scaffoldFiles(orm) {
|
|
242
|
+
return [
|
|
243
|
+
{ path: "seeder.config.ts", contents: orm === "mongoose" ? CONFIG_MONGOOSE : CONFIG_TYPEORM },
|
|
244
|
+
{ path: "src/database/factories/user.factory.ts", contents: FACTORY },
|
|
245
|
+
{
|
|
246
|
+
path: "src/database/seeders/user.seeder.ts",
|
|
247
|
+
contents: orm === "mongoose" ? SEEDER_MONGOOSE : SEEDER_TYPEORM
|
|
128
248
|
}
|
|
129
|
-
|
|
130
|
-
|
|
249
|
+
];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// src/lib/seeder/seeder.module.ts
|
|
253
|
+
var import_common2 = require("@nestjs/common");
|
|
254
|
+
|
|
255
|
+
// src/lib/seeder/seeder.service.ts
|
|
256
|
+
var import_common = require("@nestjs/common");
|
|
257
|
+
var SeederService = class {
|
|
258
|
+
constructor(seeders = [], options = {}) {
|
|
259
|
+
this.seeders = seeders;
|
|
260
|
+
this.options = options;
|
|
261
|
+
}
|
|
262
|
+
seeders;
|
|
263
|
+
options;
|
|
264
|
+
logger = new import_common.Logger("Seeder");
|
|
265
|
+
/**
|
|
266
|
+
* Runs the configured seeders. In `refresh` mode every selected seeder is
|
|
267
|
+
* dropped first (in reverse order to respect foreign-key dependencies),
|
|
268
|
+
* then all are seeded.
|
|
269
|
+
*/
|
|
270
|
+
async run() {
|
|
271
|
+
const seeders = this.getSeedersToRun();
|
|
272
|
+
if (seeders.length === 0) {
|
|
273
|
+
this.logger.warn("No seeders to run.");
|
|
274
|
+
return;
|
|
131
275
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
configModule = require(resolvedPath);
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
configModule = await import(resolvedPath);
|
|
142
|
-
}
|
|
143
|
-
return configModule;
|
|
144
|
-
};
|
|
145
|
-
try {
|
|
146
|
-
let configModule;
|
|
147
|
-
try {
|
|
148
|
-
configModule = await tryLoad(false);
|
|
149
|
-
}
|
|
150
|
-
catch (error) {
|
|
151
|
-
if (configPath.endsWith('.ts') && (error.code === 'ERR_UNKNOWN_FILE_EXTENSION' ||
|
|
152
|
-
error.code === 'ERR_REQUIRE_ESM' ||
|
|
153
|
-
error.message.includes('Unknown file extension') ||
|
|
154
|
-
error.message.includes('must use import to load ES Module'))) {
|
|
155
|
-
setupTsNode(true);
|
|
156
|
-
try {
|
|
157
|
-
configModule = await tryLoad(true);
|
|
158
|
-
}
|
|
159
|
-
catch (importError) {
|
|
160
|
-
if (importError.code === 'ERR_UNKNOWN_FILE_EXTENSION') {
|
|
161
|
-
console.error('\n❌ To load ESM TypeScript files, you must use the ts-node loader.');
|
|
162
|
-
console.error('Please run with: node --loader ts-node/esm ...\n');
|
|
163
|
-
}
|
|
164
|
-
throw importError;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
throw error;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
const config = configModule.default || configModule;
|
|
172
|
-
if (!config) {
|
|
173
|
-
throw new Error('Configuration file must export a default configuration object');
|
|
174
|
-
}
|
|
175
|
-
return config;
|
|
276
|
+
if (this.options.dryRun) {
|
|
277
|
+
this.logger.log("Dry run \u2014 the following seeders would execute:");
|
|
278
|
+
seeders.forEach((seeder, index) => {
|
|
279
|
+
this.logger.log(` ${index + 1}. ${getSeederName(seeder)}`);
|
|
280
|
+
});
|
|
281
|
+
return;
|
|
176
282
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
console.error(error.message);
|
|
180
|
-
console.error('\nMake sure your configuration file:');
|
|
181
|
-
console.error('1. Exists at the specified path');
|
|
182
|
-
console.error('2. Exports a default configuration object');
|
|
183
|
-
console.error('3. Has proper TypeScript setup if using .ts files');
|
|
184
|
-
process.exit(1);
|
|
283
|
+
if (this.options.refresh) {
|
|
284
|
+
await this.drop(seeders);
|
|
185
285
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
286
|
+
await this.seed(seeders);
|
|
287
|
+
}
|
|
288
|
+
/** Seeds the given seeders (defaults to the configured selection). */
|
|
289
|
+
async seed(seeders = this.getSeedersToRun()) {
|
|
290
|
+
for (const seeder of seeders) {
|
|
291
|
+
const name = getSeederName(seeder);
|
|
292
|
+
try {
|
|
293
|
+
this.logger.log(`Seeding: ${name}`);
|
|
294
|
+
await seeder.seed(this.options);
|
|
295
|
+
this.logger.log(`Completed: ${name}`);
|
|
296
|
+
} catch (error) {
|
|
297
|
+
this.handleError(name, "seed", error);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/** Drops the given seeders in reverse order (defaults to the selection). */
|
|
302
|
+
async drop(seeders = this.getSeedersToRun()) {
|
|
303
|
+
for (const seeder of [...seeders].reverse()) {
|
|
304
|
+
const name = getSeederName(seeder);
|
|
305
|
+
if (typeof seeder.drop !== "function") {
|
|
306
|
+
this.logger.debug(`Skipping drop for ${name} (no drop() defined)`);
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
try {
|
|
310
|
+
this.logger.log(`Dropping: ${name}`);
|
|
311
|
+
await seeder.drop(this.options);
|
|
312
|
+
this.logger.log(`Dropped: ${name}`);
|
|
313
|
+
} catch (error) {
|
|
314
|
+
this.handleError(name, "drop", error);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/** Resolves the seeders to run based on the `name` option. */
|
|
319
|
+
getSeedersToRun() {
|
|
320
|
+
const names = this.normalizeNames(this.options.name);
|
|
321
|
+
if (!names) {
|
|
322
|
+
if (this.seeders.length === 0) {
|
|
323
|
+
this.logger.warn(
|
|
324
|
+
'No seeders registered. Did you add them to the "seeders" array in your config?'
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
return this.seeders;
|
|
328
|
+
}
|
|
329
|
+
const wanted = new Set(names.map((name) => name.toLowerCase()));
|
|
330
|
+
const matched = this.seeders.filter((seeder) => {
|
|
331
|
+
const name = getSeederName(seeder).toLowerCase();
|
|
332
|
+
return wanted.has(name) || wanted.has(name.replace(/seeder$/, ""));
|
|
333
|
+
});
|
|
334
|
+
if (matched.length === 0) {
|
|
335
|
+
const available = this.seeders.map((seeder) => getSeederName(seeder));
|
|
336
|
+
this.logger.warn(
|
|
337
|
+
`No seeders matched [${names.join(", ")}]. Available seeders: ${available.length ? available.join(", ") : "(none)"}`
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
return matched;
|
|
341
|
+
}
|
|
342
|
+
handleError(name, phase, error) {
|
|
343
|
+
const message = error?.message ?? error;
|
|
344
|
+
if (this.options.continueOnError) {
|
|
345
|
+
this.logger.error(`Seeder "${name}" failed during ${phase}: ${message}`);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
throw error;
|
|
349
|
+
}
|
|
350
|
+
normalizeNames(name) {
|
|
351
|
+
if (!name) {
|
|
352
|
+
return void 0;
|
|
353
|
+
}
|
|
354
|
+
const list = (Array.isArray(name) ? name : [name]).filter(Boolean);
|
|
355
|
+
return list.length ? list : void 0;
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
SeederService = __decorateClass([
|
|
359
|
+
(0, import_common.Injectable)()
|
|
360
|
+
], SeederService);
|
|
361
|
+
|
|
362
|
+
// src/lib/seeder/seeder.module.ts
|
|
363
|
+
var SEEDER_MODULE_OPTIONS = "SEEDER_MODULE_OPTIONS";
|
|
364
|
+
var SeederModule = class {
|
|
365
|
+
static register(options) {
|
|
366
|
+
return {
|
|
367
|
+
module: SeederModule,
|
|
368
|
+
imports: options.imports || [],
|
|
369
|
+
providers: [
|
|
370
|
+
...options.providers || [],
|
|
371
|
+
...options.seeders || [],
|
|
372
|
+
{
|
|
373
|
+
provide: SeederService,
|
|
374
|
+
useFactory: (...seeders) => {
|
|
375
|
+
return new SeederService(seeders, options);
|
|
376
|
+
},
|
|
377
|
+
inject: options.seeders || []
|
|
203
378
|
}
|
|
204
|
-
|
|
205
|
-
|
|
379
|
+
]
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
static forRootAsync(options) {
|
|
383
|
+
return {
|
|
384
|
+
module: SeederModule,
|
|
385
|
+
global: options.isGlobal,
|
|
386
|
+
imports: options.imports || [],
|
|
387
|
+
providers: [
|
|
388
|
+
...this.createAsyncProviders(options),
|
|
389
|
+
{
|
|
390
|
+
provide: SeederService,
|
|
391
|
+
useFactory: (seederOptions, ...seeders) => {
|
|
392
|
+
return new SeederService(seeders, seederOptions);
|
|
393
|
+
},
|
|
394
|
+
inject: [SEEDER_MODULE_OPTIONS, ...options.inject || []]
|
|
206
395
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
await app.close();
|
|
214
|
-
console.log('✅ Seeding completed successfully!');
|
|
215
|
-
process.exit(0);
|
|
396
|
+
]
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
static createAsyncProviders(options) {
|
|
400
|
+
if (options.useExisting || options.useFactory) {
|
|
401
|
+
return [this.createAsyncOptionsProvider(options)];
|
|
216
402
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
403
|
+
return [
|
|
404
|
+
this.createAsyncOptionsProvider(options),
|
|
405
|
+
{
|
|
406
|
+
provide: options.useClass,
|
|
407
|
+
useClass: options.useClass
|
|
408
|
+
}
|
|
409
|
+
];
|
|
410
|
+
}
|
|
411
|
+
static createAsyncOptionsProvider(options) {
|
|
412
|
+
if (options.useFactory) {
|
|
413
|
+
return {
|
|
414
|
+
provide: SEEDER_MODULE_OPTIONS,
|
|
415
|
+
useFactory: options.useFactory,
|
|
416
|
+
inject: options.inject || []
|
|
417
|
+
};
|
|
225
418
|
}
|
|
226
|
-
}
|
|
227
|
-
const seeder = (options) => {
|
|
228
419
|
return {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (argv.r || argv.refresh) {
|
|
233
|
-
cliOptions.refresh = true;
|
|
234
|
-
}
|
|
235
|
-
if (argv.n || argv.name) {
|
|
236
|
-
cliOptions.name = argv.n || argv.name;
|
|
237
|
-
}
|
|
238
|
-
if (argv.d || argv.dummyData) {
|
|
239
|
-
cliOptions.dummyData = argv.d || argv.dummyData;
|
|
240
|
-
}
|
|
241
|
-
extraOptions = Object.assign(extraOptions, cliOptions);
|
|
242
|
-
const app = await core_1.NestFactory.createApplicationContext(seeder_module_1.SeederModule.register({
|
|
243
|
-
...options,
|
|
244
|
-
...extraOptions,
|
|
245
|
-
}));
|
|
246
|
-
const seedersService = app.get(seeder_service_1.SeederService);
|
|
247
|
-
await seedersService.run();
|
|
248
|
-
await app.close();
|
|
249
|
-
},
|
|
420
|
+
provide: SEEDER_MODULE_OPTIONS,
|
|
421
|
+
useFactory: async (optionsFactory) => await optionsFactory.createSeederOptions(),
|
|
422
|
+
inject: [options.useExisting || options.useClass]
|
|
250
423
|
};
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
SeederModule = __decorateClass([
|
|
427
|
+
(0, import_common2.Module)({})
|
|
428
|
+
], SeederModule);
|
|
429
|
+
|
|
430
|
+
// src/cli.ts
|
|
431
|
+
var log = {
|
|
432
|
+
info: (msg) => console.log(msg),
|
|
433
|
+
error: (msg) => console.error(`\x1B[31m${msg}\x1B[0m`),
|
|
434
|
+
success: (msg) => console.log(`\x1B[32m${msg}\x1B[0m`),
|
|
435
|
+
dim: (msg) => console.log(`\x1B[2m${msg}\x1B[0m`)
|
|
251
436
|
};
|
|
252
|
-
|
|
437
|
+
function parseContext(raw) {
|
|
438
|
+
if (!raw) {
|
|
439
|
+
return void 0;
|
|
440
|
+
}
|
|
441
|
+
try {
|
|
442
|
+
return JSON.parse(raw);
|
|
443
|
+
} catch {
|
|
444
|
+
throw new Error(`--context must be valid JSON. Received: ${raw}`);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
async function createContext(configOption) {
|
|
448
|
+
const configPath = resolveConfigPath(configOption);
|
|
449
|
+
if (!configPath) {
|
|
450
|
+
throw new Error(
|
|
451
|
+
configOption ? `Config file not found: ${configOption}` : 'No seeder config found. Create a seeder.config.ts (try "nest-seed init") or pass one with --config.'
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
log.dim(`Using config: ${path2.relative(process.cwd(), configPath)}`);
|
|
455
|
+
const config = await loadSeederConfig(configPath);
|
|
456
|
+
return { config, configPath };
|
|
457
|
+
}
|
|
458
|
+
async function runCommand(argv) {
|
|
459
|
+
const { config } = await createContext(argv.config);
|
|
460
|
+
const options = {
|
|
461
|
+
refresh: argv.refresh,
|
|
462
|
+
name: argv.name,
|
|
463
|
+
dryRun: argv["dry-run"],
|
|
464
|
+
continueOnError: argv["continue-on-error"],
|
|
465
|
+
dummyData: argv["dummy-data"],
|
|
466
|
+
context: parseContext(argv.context)
|
|
467
|
+
};
|
|
468
|
+
if (options.refresh) log.info("\u{1F504} Refresh mode: dropping data before seeding");
|
|
469
|
+
if (options.dryRun) log.info("\u{1F50E} Dry run: no data will be written");
|
|
470
|
+
if (options.name) {
|
|
471
|
+
const names = Array.isArray(options.name) ? options.name : [options.name];
|
|
472
|
+
log.info(`\u{1F3AF} Seeders: ${names.join(", ")}`);
|
|
473
|
+
}
|
|
474
|
+
const app = await import_core.NestFactory.createApplicationContext(
|
|
475
|
+
SeederModule.register({ ...config, ...options }),
|
|
476
|
+
{ logger: ["error", "warn", "log"] }
|
|
477
|
+
);
|
|
478
|
+
try {
|
|
479
|
+
await app.get(SeederService).run();
|
|
480
|
+
} finally {
|
|
481
|
+
await app.close();
|
|
482
|
+
}
|
|
483
|
+
log.success("\u2705 Seeding completed successfully!");
|
|
484
|
+
}
|
|
485
|
+
async function listCommand(argv) {
|
|
486
|
+
const { config } = await createContext(argv.config);
|
|
487
|
+
const seeders = config.seeders ?? [];
|
|
488
|
+
if (seeders.length === 0) {
|
|
489
|
+
log.info("No seeders registered in the config.");
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
log.info(`Registered seeders (${seeders.length}):`);
|
|
493
|
+
seeders.forEach((seeder, index) => {
|
|
494
|
+
const className = seeder?.name ?? "Unknown";
|
|
495
|
+
const stableName = getSeederName(seeder);
|
|
496
|
+
const alias = stableName !== className ? ` (--name ${stableName})` : "";
|
|
497
|
+
log.info(` ${index + 1}. ${className}${alias}`);
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
function initCommand(argv) {
|
|
501
|
+
const orm = argv.orm;
|
|
502
|
+
const force = Boolean(argv.force);
|
|
503
|
+
const files = scaffoldFiles(orm);
|
|
504
|
+
let created = 0;
|
|
505
|
+
let skipped = 0;
|
|
506
|
+
for (const file of files) {
|
|
507
|
+
const absolute = path2.resolve(process.cwd(), file.path);
|
|
508
|
+
if (fs2.existsSync(absolute) && !force) {
|
|
509
|
+
log.dim(`\u2022 skipped (exists): ${file.path}`);
|
|
510
|
+
skipped++;
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
fs2.mkdirSync(path2.dirname(absolute), { recursive: true });
|
|
514
|
+
fs2.writeFileSync(absolute, file.contents);
|
|
515
|
+
log.success(`\u2022 created: ${file.path}`);
|
|
516
|
+
created++;
|
|
517
|
+
}
|
|
518
|
+
log.info(`
|
|
519
|
+
Scaffolded ${created} file(s)${skipped ? `, skipped ${skipped}` : ""}.`);
|
|
520
|
+
log.info("\nNext steps:");
|
|
521
|
+
log.info(" 1. Adjust seeder.config.ts for your database and entities.");
|
|
522
|
+
log.info(" 2. Add a script to package.json:");
|
|
523
|
+
log.dim(' "seed": "nest-seed"');
|
|
524
|
+
log.info(" 3. Run it:");
|
|
525
|
+
log.dim(" npm run seed");
|
|
526
|
+
if (skipped && !force) {
|
|
527
|
+
log.dim("\nUse --force to overwrite existing files.");
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
async function main() {
|
|
531
|
+
await (0, import_yargs.default)((0, import_helpers.hideBin)(process.argv)).scriptName("nest-seed").usage("$0 [command] [options]").command(
|
|
532
|
+
["$0", "run"],
|
|
533
|
+
"Run seeders (default command)",
|
|
534
|
+
(y) => y.option("config", {
|
|
535
|
+
alias: "c",
|
|
536
|
+
type: "string",
|
|
537
|
+
describe: "Path to the seeder config file (auto-detected if omitted)"
|
|
538
|
+
}).option("refresh", {
|
|
539
|
+
alias: "r",
|
|
540
|
+
type: "boolean",
|
|
541
|
+
default: false,
|
|
542
|
+
describe: "Drop data before seeding"
|
|
543
|
+
}).option("name", {
|
|
544
|
+
alias: "n",
|
|
545
|
+
type: "array",
|
|
546
|
+
string: true,
|
|
547
|
+
describe: "Run only the named seeder(s)"
|
|
548
|
+
}).option("dry-run", {
|
|
549
|
+
type: "boolean",
|
|
550
|
+
default: false,
|
|
551
|
+
describe: "Show which seeders would run without executing them"
|
|
552
|
+
}).option("continue-on-error", {
|
|
553
|
+
type: "boolean",
|
|
554
|
+
default: false,
|
|
555
|
+
describe: "Keep going when a seeder fails"
|
|
556
|
+
}).option("context", {
|
|
557
|
+
type: "string",
|
|
558
|
+
describe: "JSON forwarded to every seeder via options.context"
|
|
559
|
+
}).option("dummy-data", {
|
|
560
|
+
alias: "d",
|
|
561
|
+
type: "boolean",
|
|
562
|
+
default: false,
|
|
563
|
+
describe: "[deprecated] forwarded as options.dummyData"
|
|
564
|
+
}),
|
|
565
|
+
runCommand
|
|
566
|
+
).command(
|
|
567
|
+
"init",
|
|
568
|
+
"Scaffold a seeder config, factory and seeder",
|
|
569
|
+
(y) => y.option("orm", {
|
|
570
|
+
type: "string",
|
|
571
|
+
choices: ["typeorm", "mongoose"],
|
|
572
|
+
default: "typeorm",
|
|
573
|
+
describe: "Which ORM template to scaffold"
|
|
574
|
+
}).option("force", {
|
|
575
|
+
type: "boolean",
|
|
576
|
+
default: false,
|
|
577
|
+
describe: "Overwrite existing files"
|
|
578
|
+
}),
|
|
579
|
+
(argv) => initCommand(argv)
|
|
580
|
+
).command(
|
|
581
|
+
"list",
|
|
582
|
+
"List the seeders registered in the config",
|
|
583
|
+
(y) => y.option("config", {
|
|
584
|
+
alias: "c",
|
|
585
|
+
type: "string",
|
|
586
|
+
describe: "Path to the seeder config file (auto-detected if omitted)"
|
|
587
|
+
}),
|
|
588
|
+
listCommand
|
|
589
|
+
).example("$0", "Run all seeders using the auto-detected config").example("$0 --refresh", "Drop and reseed all data").example("$0 --name users posts", 'Run only the "users" and "posts" seeders').example("$0 init", "Scaffold starter files").example("$0 list", "List available seeders").strict().alias("h", "help").alias("v", "version").fail((msg, err) => {
|
|
590
|
+
if (err) throw err;
|
|
591
|
+
log.error(`
|
|
592
|
+
${msg}`);
|
|
593
|
+
console.error('\nRun "nest-seed --help" for usage.');
|
|
594
|
+
process.exit(1);
|
|
595
|
+
}).parseAsync();
|
|
596
|
+
}
|
|
597
|
+
async function bootstrap() {
|
|
598
|
+
log.info("\u{1F331} nest-seeder");
|
|
599
|
+
try {
|
|
600
|
+
await main();
|
|
601
|
+
process.exit(0);
|
|
602
|
+
} catch (error) {
|
|
603
|
+
log.error(`
|
|
604
|
+
\u274C ${error?.message ?? error}`);
|
|
605
|
+
if (error?.stack && process.env.NEST_SEEDER_DEBUG) {
|
|
606
|
+
console.error(error.stack);
|
|
607
|
+
} else {
|
|
608
|
+
log.dim("Set NEST_SEEDER_DEBUG=1 for a full stack trace.");
|
|
609
|
+
}
|
|
610
|
+
process.exit(1);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
253
613
|
if (require.main === module) {
|
|
254
|
-
|
|
614
|
+
bootstrap();
|
|
255
615
|
}
|
|
256
616
|
//# sourceMappingURL=cli.js.map
|