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