adminforth 2.25.0-test.2 → 2.25.0-test.20

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.
@@ -16,7 +16,8 @@ async function bundle() {
16
16
  `);
17
17
 
18
18
  } catch (e) {
19
- console.log(`Running budndle failed`, e);
19
+ console.log(`Running bundle failed`, e);
20
+ throw new Error(`Failed to bundle admin SPA: ${e.message}`);
20
21
  }
21
22
  }
22
23
 
@@ -1,3 +1,14 @@
1
+
2
+ {{#unless useNpm}}
3
+ FROM devforth/node20-pnpm:latest
4
+ WORKDIR /code/
5
+ ADD package.json pnpm-lock.yaml pnpm-workspace.yaml /code/
6
+ RUN pnpm i
7
+ ADD . /code/
8
+ RUN pnpm exec adminforth bundle
9
+ CMD ["sh", "-c", "pnpm migrate:prod && pnpm prod"]
10
+ {{/unless}}
11
+ {{#if useNpm}}
1
12
  FROM node:{{nodeMajor}}-slim
2
13
  WORKDIR /code/
3
14
  ADD package.json package-lock.json /code/
@@ -5,3 +16,4 @@ RUN npm ci
5
16
  ADD . /code/
6
17
  RUN npx adminforth bundle
7
18
  CMD ["sh", "-c", "npm run migrate:prod && npm run prod"]
19
+ {{/if}}
@@ -8,6 +8,7 @@
8
8
  "license": "ISC",
9
9
  "description": "",
10
10
  "scripts": {
11
+ {{#unless useNpm}}
11
12
  "dev": "pnpm _env:dev tsx watch index.ts",
12
13
  "prod": "pnpm _env:prod tsx index.ts",
13
14
  "start": "pnpm dev",
@@ -16,6 +17,17 @@
16
17
  "migrate:prod": "pnpm _env:prod npx --yes prisma migrate deploy",
17
18
  "_env:dev": "dotenvx run -f .env -f .env.local --",
18
19
  "_env:prod": "dotenvx run -f .env.prod --"
20
+ {{/unless}}
21
+ {{#if useNpm}}
22
+ "dev": "npm run _env:dev -- tsx watch index.ts",
23
+ "prod": "npm run _env:prod -- tsx index.ts",
24
+ "start": "npm run dev",
25
+ "makemigration": "npm run _env:dev -- npx --yes prisma migrate dev --create-only",
26
+ "migrate:local": "npm run _env:dev -- npx --yes prisma migrate deploy",
27
+ "migrate:prod": "npm run _env:prod -- npx --yes prisma migrate deploy",
28
+ "_env:dev": "dotenvx run -f .env -f .env.local --",
29
+ "_env:prod": "dotenvx run -f .env.prod --"
30
+ {{/if}}
19
31
  },
20
32
  "engines": {
21
33
  "node": ">=20"
@@ -0,0 +1,8 @@
1
+ lockfileVersion: '9.0'
2
+
3
+ settings:
4
+ autoInstallPeers: true
5
+ excludeLinksFromLockfile: false
6
+
7
+ importers:
8
+ .: {}
@@ -1,7 +1,25 @@
1
1
  ## Starting the application
2
2
 
3
3
  Install dependencies:
4
+ {{#if useNpm}}
5
+ ```bash
6
+ npm i
7
+ ```
8
+
9
+ Migrate the database:
10
+
11
+ ```bash
12
+ npm run migrate:local
13
+ ```
14
+
15
+ Start the server:
16
+
17
+ ```bash
18
+ npm run dev
19
+ ```
20
+ {{/if}}
4
21
 
22
+ {{#unless useNpm}}
5
23
  ```bash
6
24
  pnpm i
7
25
  ```
@@ -17,6 +35,7 @@ Start the server:
17
35
  ```bash
18
36
  pnpm dev
19
37
  ```
38
+ {{/unless}}
20
39
 
21
40
  {{#if prismaDbUrl}}
22
41
  ## Changing schema
@@ -26,7 +45,7 @@ Open `schema.prisma` and change schema as needed: add new tables, columns, etc (
26
45
  Run the following command to generate a new migration and apply it instantly in local database:
27
46
 
28
47
  ```bash
29
- pnpm makemigration -- --name <name_of_changes>
48
+ pnpm makemigration --name <name_of_changes>
30
49
  ```
31
50
 
32
51
  Your colleagues will need to pull the changes and run `pnpm migrate:local` to apply the migration in their local database.
@@ -44,6 +44,7 @@ export function parseArgumentsIntoOptions(rawArgs) {
44
44
  {
45
45
  '--app-name': String,
46
46
  '--db': String,
47
+ '--use-npm': Boolean,
47
48
  // you can add more flags here if needed
48
49
  },
49
50
  {
@@ -54,6 +55,7 @@ export function parseArgumentsIntoOptions(rawArgs) {
54
55
  return {
55
56
  appName: args['--app-name'],
56
57
  db: args['--db'],
58
+ useNpm: args['--use-npm'],
57
59
  };
58
60
  }
59
61
 
@@ -78,11 +80,25 @@ export async function promptForMissingOptions(options) {
78
80
  });
79
81
  };
80
82
 
83
+ if (!options.useNpm) {
84
+ questions.push({
85
+ type: 'select',
86
+ name: 'useNpm',
87
+ message: 'Select your package manager >',
88
+ choices: [
89
+ { name: 'pnpm', value: false },
90
+ { name: 'npm', value: true },
91
+ ],
92
+ default: false,
93
+ });
94
+ }
95
+
81
96
  const answers = await inquirer.prompt(questions);
82
97
  return {
83
98
  ...options,
84
99
  appName: options.appName || answers.appName,
85
100
  db: options.db || answers.db,
101
+ useNpm: options.useNpm || answers.useNpm,
86
102
  };
87
103
  }
88
104
 
@@ -230,7 +246,7 @@ async function scaffoldProject(ctx, options, cwd) {
230
246
  await fse.copy(sourceAssetsDir, targetAssetsDir);
231
247
 
232
248
  // Write templated files
233
- await writeTemplateFiles(dirname, projectDir, {
249
+ await writeTemplateFiles(dirname, projectDir, options.useNpm, {
234
250
  dbUrl: connectionString.toString(),
235
251
  dbUrlProd: connectionStringProd,
236
252
  prismaDbUrl,
@@ -244,7 +260,7 @@ async function scaffoldProject(ctx, options, cwd) {
244
260
  return projectDir; // Return the new directory path
245
261
  }
246
262
 
247
- async function writeTemplateFiles(dirname, cwd, options) {
263
+ async function writeTemplateFiles(dirname, cwd, useNpm, options) {
248
264
  const {
249
265
  dbUrl, prismaDbUrl, appName, provider, nodeMajor,
250
266
  dbUrlProd, prismaDbUrlProd, sqliteFile
@@ -268,14 +284,6 @@ async function writeTemplateFiles(dirname, cwd, options) {
268
284
  dest: 'prisma.config.ts',
269
285
  data: {},
270
286
  },
271
- {
272
- src: 'package.json.hbs',
273
- dest: 'package.json',
274
- data: {
275
- appName,
276
- adminforthVersion: adminforthVersion,
277
- },
278
- },
279
287
  {
280
288
  src: 'index.ts.hbs',
281
289
  dest: 'index.ts',
@@ -304,7 +312,7 @@ async function writeTemplateFiles(dirname, cwd, options) {
304
312
  {
305
313
  src: 'readme.md.hbs',
306
314
  dest: 'README.md',
307
- data: { dbUrl, prismaDbUrl, appName, sqliteFile },
315
+ data: { dbUrl, prismaDbUrl, appName, sqliteFile, useNpm },
308
316
  },
309
317
  {
310
318
  // We'll write .env using the same content as .env.sample
@@ -317,21 +325,11 @@ async function writeTemplateFiles(dirname, cwd, options) {
317
325
  dest: 'resources/adminuser.ts',
318
326
  data: {},
319
327
  },
320
- {
321
- src: 'custom/package.json.hbs',
322
- dest: 'custom/package.json',
323
- data: {},
324
- },
325
328
  {
326
329
  src: 'custom/tsconfig.json.hbs',
327
330
  dest: 'custom/tsconfig.json',
328
331
  data: {},
329
332
  },
330
- {
331
- src: 'Dockerfile.hbs',
332
- dest: 'Dockerfile',
333
- data: { nodeMajor },
334
- },
335
333
  {
336
334
  src: '.dockerignore.hbs',
337
335
  dest: '.dockerignore',
@@ -340,12 +338,41 @@ async function writeTemplateFiles(dirname, cwd, options) {
340
338
  },
341
339
  },
342
340
  {
343
- src: 'pnpm-workspace.yaml.hbs',
344
- dest: 'pnpm-workspace.yaml',
345
- data: {},
341
+ src: 'Dockerfile.hbs',
342
+ dest: 'Dockerfile',
343
+ data: { nodeMajor, useNpm },
344
+ },
345
+ {
346
+ src: 'package.json.hbs',
347
+ dest: 'package.json',
348
+ data: {
349
+ appName,
350
+ adminforthVersion: adminforthVersion,
351
+ useNpm
352
+ },
353
+ },
354
+ {
355
+ src: 'custom/package.json.hbs',
356
+ dest: 'custom/package.json',
357
+ data: {}
346
358
  }
347
359
  ];
348
360
 
361
+ if (!useNpm) {
362
+ templateTasks.push(
363
+ {
364
+ src: 'pnpm_templates/pnpm-workspace.yaml.hbs',
365
+ dest: 'pnpm-workspace.yaml',
366
+ data: {},
367
+ },
368
+ {
369
+ src: 'pnpm_templates/pnpm-lock.yaml.hbs',
370
+ dest: 'custom/pnpm-lock.yaml',
371
+ data: {},
372
+ }
373
+ )
374
+ }
375
+
349
376
  for (const task of templateTasks) {
350
377
  // If a condition is specified and false, skip this file
351
378
  if (task.condition === false) continue;
@@ -363,7 +390,7 @@ async function writeTemplateFiles(dirname, cwd, options) {
363
390
  }
364
391
  }
365
392
 
366
- async function installDependencies(ctx, cwd) {
393
+ async function installDependenciesPnpm(ctx, cwd) {
367
394
  const isWindows = process.platform === 'win32';
368
395
 
369
396
  const nodeBinary = process.execPath;
@@ -380,10 +407,28 @@ async function installDependencies(ctx, cwd) {
380
407
  await execAsync(`${nodeBinary} ${npmPath} install`, { cwd: customDir, env: { PATH: process.env.PATH } }),
381
408
  ]);
382
409
  }
383
- // console.log(chalk.dim(`Dependencies installed in ${cwd} and ${customDir}: \n${res[0].stdout}${res[1].stdout}`));
384
410
  }
385
411
 
386
- function generateFinalInstructions(skipPrismaSetup, options) {
412
+ async function installDependenciesNpm(ctx, cwd) {
413
+ const isWindows = process.platform === 'win32';
414
+
415
+ const nodeBinary = process.execPath;
416
+ const npmPath = path.join(path.dirname(nodeBinary), isWindows ? 'npm.cmd' : 'npm');
417
+ const customDir = ctx.customDir;
418
+ if (isWindows) {
419
+ const res = await Promise.all([
420
+ await execAsync(`npm install`, { cwd, env: { PATH: process.env.PATH } }),
421
+ await execAsync(`npm install`, { cwd: customDir, env: { PATH: process.env.PATH } }),
422
+ ]);
423
+ } else {
424
+ const res = await Promise.all([
425
+ await execAsync(`${nodeBinary} ${npmPath} install`, { cwd, env: { PATH: process.env.PATH } }),
426
+ await execAsync(`${nodeBinary} ${npmPath} install`, { cwd: customDir, env: { PATH: process.env.PATH } }),
427
+ ]);
428
+ }
429
+ }
430
+
431
+ function generateFinalInstructionsPnpm(skipPrismaSetup, options) {
387
432
  let instruction = '⏭️ Run the following commands to get started:\n';
388
433
  if (!skipPrismaSetup)
389
434
  instruction += `
@@ -404,6 +449,27 @@ function generateFinalInstructions(skipPrismaSetup, options) {
404
449
  return instruction;
405
450
  }
406
451
 
452
+ function generateFinalInstructionsNpm(skipPrismaSetup, options) {
453
+ let instruction = '⏭️ Run the following commands to get started:\n';
454
+ if (!skipPrismaSetup)
455
+ instruction += `
456
+ ${chalk.dim('// Go to the project directory')}
457
+ ${chalk.dim('$')}${chalk.cyan(` cd ${options.appName}`)}\n`;
458
+
459
+ instruction += `
460
+ ${chalk.dim('// Generate and apply initial migration')}
461
+ ${chalk.dim('$')}${chalk.cyan(' npm run makemigration -- --name init && npm run migrate:local')}\n`;
462
+
463
+ instruction += `
464
+ ${chalk.dim('// Start dev server with tsx watch for hot-reloading')}
465
+ ${chalk.dim('$')}${chalk.cyan(' npm run dev')}\n
466
+ `;
467
+
468
+ instruction += '😉 Happy coding!';
469
+
470
+ return instruction;
471
+ }
472
+
407
473
  function renderHBSTemplate(templatePath, data) {
408
474
  // Example: renderHBRTemplate('path/to/template.hbs', {name: 'John Doe'})
409
475
  const template = fs.readFileSync(templatePath, 'utf-8');
@@ -430,13 +496,23 @@ export function prepareWorkflow(options) {
430
496
  },
431
497
  {
432
498
  title: '📦 Installing dependencies...',
433
- task: async (ctx) => installDependencies(ctx, ctx.projectDir)
499
+ task: async (ctx) => {
500
+ if (options.useNpm) {
501
+ await installDependenciesNpm(ctx, ctx.projectDir);
502
+ } else {
503
+ await installDependenciesPnpm(ctx, ctx.projectDir);
504
+ }
505
+ }
434
506
  },
435
507
  {
436
508
  title: '📝 Preparing final instructions...',
437
509
  task: (ctx) => {
438
510
  console.log(chalk.green(`✅ Successfully created your new Adminforth project in ${ctx.projectDir}!\n`));
439
- console.log(generateFinalInstructions(ctx.skipPrismaSetup, options));
511
+ if (options.useNpm) {
512
+ console.log(generateFinalInstructionsNpm(ctx.skipPrismaSetup, options));
513
+ } else {
514
+ console.log(generateFinalInstructionsPnpm(ctx.skipPrismaSetup, options));
515
+ }
440
516
  console.log('\n\n');
441
517
  }
442
518
  }
@@ -158,7 +158,7 @@ async function installDependencies(ctx, cwd) {
158
158
  const customDir = ctx.customDir;
159
159
 
160
160
  await Promise.all([
161
- await execa("pnpm", ["install", "--no-package-lock"], { cwd }),
161
+ await execa("pnpm", ["install"], { cwd }),
162
162
  await execa("pnpm", ["install"], { cwd: customDir }),
163
163
  ]);
164
164
  }
@@ -15,7 +15,8 @@ declare class CodeInjector implements ICodeInjector {
15
15
  registerCustomComponent(filePath: string): void;
16
16
  cleanup(): void;
17
17
  constructor(adminforth: any);
18
- runPnpmShell({ command, cwd, envOverrides }: {
18
+ doesUserHasPnpmLockFile(dir: string): Promise<boolean>;
19
+ runPackageManagerShell({ command, cwd, envOverrides }: {
19
20
  command: string;
20
21
  cwd: string;
21
22
  envOverrides?: {
@@ -1 +1 @@
1
- {"version":3,"file":"codeInjector.d.ts","sourceRoot":"","sources":["../../modules/codeInjector.ts"],"names":[],"mappings":"AAQA,OAAO,UAAwC,MAAM,aAAa,CAAC;AAEnE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAmEjD,cAAM,YAAa,YAAW,aAAa;IAEzC,WAAW,QAAM;IACjB,UAAU,EAAE,UAAU,CAAC;IACvB,iBAAiB,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAM;IAClD,gBAAgB,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAM;IACjD,aAAa,EAAE,MAAM,CAAQ;IAE7B,UAAU,IAAI,MAAM;IAQd,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE;IA8BpC,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAK/C,OAAO;gBAMK,UAAU,KAAA;IAWhB,YAAY,CAAC,EAAC,OAAO,EAAE,GAAG,EAAE,YAAiB,EAAC,EAAE;QACpD,OAAO,EAAE,MAAM,CAAC;QAChB,GAAG,EAAE,MAAM,CAAC;QACZ,YAAY,CAAC,EAAE;YAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAA;KACzC;IA8CK,QAAQ;IAUd,WAAW;IAIL,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAgFhE,SAAS;IAQH,cAAc,CAAC,EAAE,YAAY,EAAE,EAAE;QAAE,YAAY,EAAE,MAAM,EAAE,CAAA;KAAE;IAgB3D,yBAAyB,CAAC,OAAO,KAAA;IAMjC,cAAc;IA4cd,iBAAiB,CAAC,EAAE,IAAA;IA4CpB,4BAA4B,CAAC,EAAE,mBAAmB,EAAE,WAAW,EAAE;;;KAAA;IAwEjE,WAAW,CAAC,QAAQ,EAAE,MAAM;IAW5B,kBAAkB,CAAC,UAAU,GAAE,MAA0B,EAAE,QAAQ,GAAE,MAAM,EAAO;IAyBlF,SAAS,CAAC,EAAE,SAAiB,EAAE,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE;CA0H9D;AAED,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"codeInjector.d.ts","sourceRoot":"","sources":["../../modules/codeInjector.ts"],"names":[],"mappings":"AAQA,OAAO,UAAwC,MAAM,aAAa,CAAC;AAEnE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAmEjD,cAAM,YAAa,YAAW,aAAa;IAEzC,WAAW,QAAM;IACjB,UAAU,EAAE,UAAU,CAAC;IACvB,iBAAiB,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAM;IAClD,gBAAgB,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAM;IACjD,aAAa,EAAE,MAAM,CAAQ;IAE7B,UAAU,IAAI,MAAM;IAQd,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE;IA8BpC,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAK/C,OAAO;gBAMK,UAAU,KAAA;IAYhB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAqBtD,sBAAsB,CAAC,EAAC,OAAO,EAAE,GAAG,EAAE,YAAiB,EAAC,EAAE;QAC9D,OAAO,EAAE,MAAM,CAAC;QAChB,GAAG,EAAE,MAAM,CAAC;QACZ,YAAY,CAAC,EAAE;YAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAA;KACzC;IAuDK,QAAQ;IAUd,WAAW;IAIL,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAgFhE,SAAS;IAQH,cAAc,CAAC,EAAE,YAAY,EAAE,EAAE;QAAE,YAAY,EAAE,MAAM,EAAE,CAAA;KAAE;IAgB3D,yBAAyB,CAAC,OAAO,KAAA;IAMjC,cAAc;IA8cd,iBAAiB,CAAC,EAAE,IAAA;IA4CpB,4BAA4B,CAAC,EAAE,mBAAmB,EAAE,WAAW,EAAE;;;KAAA;IAwEjE,WAAW,CAAC,QAAQ,EAAE,MAAM;IAW5B,kBAAkB,CAAC,UAAU,GAAE,MAA0B,EAAE,QAAQ,GAAE,MAAM,EAAO;IAyBlF,SAAS,CAAC,EAAE,SAAiB,EAAE,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE;CA2H9D;AAED,eAAe,YAAY,CAAC"}
@@ -77,7 +77,7 @@ class CodeInjector {
77
77
  const uniqueIcons = Array.from(new Set(icons));
78
78
  const collections = new Set(icons.map((icon) => icon.split(':')[0]));
79
79
  const iconPackageNames = Array.from(collections).map((collection) => `@iconify-prerendered/vue-${collection}`);
80
- const iconPackages = (await Promise.allSettled(iconPackageNames.map(async (pkg) => ({ pkg: await import(this.spaTmpPath() + '/node_modules/' + pkg), name: pkg }))));
80
+ const iconPackages = (await Promise.allSettled(iconPackageNames.map(async (pkg) => ({ pkg: await import(path.join(this.spaTmpPath(), 'node_modules', pkg)), name: pkg }))));
81
81
  const loadedIconPackages = iconPackages.filter(isFulfilled).map((res) => res.value).reduce((acc, { pkg, name }) => {
82
82
  acc[name.slice(`@iconify-prerendered/vue-`.length)] = pkg;
83
83
  return acc;
@@ -117,25 +117,56 @@ class CodeInjector {
117
117
  process.exit();
118
118
  }));
119
119
  }
120
- async runPnpmShell({ command, cwd, envOverrides = {} }) {
120
+ async doesUserHasPnpmLockFile(dir) {
121
+ const usersPackagePath = path.join(dir, 'package.json');
122
+ let packageContent = null;
123
+ try {
124
+ packageContent = JSON.parse(await fs.promises.readFile(usersPackagePath, 'utf-8'));
125
+ }
126
+ catch (e) {
127
+ // user package.json does not exist, user does not have custom components
128
+ }
129
+ if (packageContent) {
130
+ const lockPath = path.join(dir, 'pnpm-lock.yaml');
131
+ let lock = null;
132
+ try {
133
+ lock = yaml.parse(await fs.promises.readFile(lockPath, 'utf-8'));
134
+ return true;
135
+ }
136
+ catch (e) {
137
+ return false;
138
+ }
139
+ }
140
+ return false;
141
+ }
142
+ async runPackageManagerShell({ command, cwd, envOverrides = {} }) {
121
143
  const nodeBinary = process.execPath; // Path to the Node.js binary running this script
122
- // On Windows, pnpm is pnpm.cmd, on Unix systems it's pnpm
123
- const pnpmExecutable = process.platform === 'win32' ? 'pnpm.cmd' : 'pnpm';
124
- const pnpmPath = path.join(path.dirname(nodeBinary), pnpmExecutable); // Path to the pnpm executable
144
+ const doesUserHavePnpmLock = await this.doesUserHasPnpmLockFile(this.adminforth.config.customization.customComponentsDir);
145
+ // On Windows, npm/pnpm is npm/pnpm.cmd, on Unix systems it's npm/pnpm
146
+ let packageExecutable;
147
+ if (doesUserHavePnpmLock) {
148
+ afLogger.trace(`User has pnpm-lock.yaml, using pnpm for installing custom components`);
149
+ packageExecutable = process.platform === 'win32' ? 'pnpm.cmd' : 'pnpm';
150
+ }
151
+ else {
152
+ afLogger.trace(`User does not have pnpm-lock.yaml, falling back to npm for installing custom components`);
153
+ packageExecutable = process.platform === 'win32' ? 'npm.cmd' : 'npm';
154
+ }
155
+ const packagePath = path.join(path.dirname(nodeBinary), packageExecutable); // Path to the package executable
125
156
  const env = Object.assign(Object.assign({ VITE_ADMINFORTH_PUBLIC_PATH: this.adminforth.config.baseUrl, FORCE_COLOR: '1' }, process.env), envOverrides);
126
- afLogger.trace(`⚙️ exec: pnpm ${command}`);
127
- afLogger.trace(`🪲 pnpm ${command} cwd: ${cwd}`);
157
+ afLogger.trace(`⚙️ exec: ${packageExecutable} ${command}`);
158
+ afLogger.trace(`🪲 ${packageExecutable} ${command} cwd: ${cwd}`);
128
159
  let execCommand;
129
160
  if (process.platform === 'win32') {
130
161
  // Quote path if it contains spaces
131
- const quotedPnpmPath = pnpmPath.includes(' ') ? `"${pnpmPath}"` : pnpmPath;
132
- execCommand = `${quotedPnpmPath} ${command}`;
162
+ const quotedPackagePath = packagePath.includes(' ') ? `"${packagePath}"` : packagePath;
163
+ execCommand = `${quotedPackagePath} ${command}`;
133
164
  }
134
165
  else {
135
166
  // Quote paths that contain spaces (for Unix systems)
136
167
  const quotedNodeBinary = nodeBinary.includes(' ') ? `"${nodeBinary}"` : nodeBinary;
137
- const quotedPnpmPath = pnpmPath.includes(' ') ? `"${pnpmPath}"` : pnpmPath;
138
- execCommand = `${quotedNodeBinary} ${quotedPnpmPath} ${command}`;
168
+ const quotedPackagePath = packagePath.includes(' ') ? `"${packagePath}"` : packagePath;
169
+ execCommand = `${quotedNodeBinary} ${quotedPackagePath} ${command}`;
139
170
  }
140
171
  const execOptions = {
141
172
  cwd,
@@ -145,9 +176,9 @@ class CodeInjector {
145
176
  execOptions.shell = true;
146
177
  }
147
178
  const { stderr: err } = await execAsync(execCommand, execOptions);
148
- afLogger.trace(`pnpm ${command} done in`);
179
+ afLogger.trace(`${packageExecutable} ${command} done in`);
149
180
  if (err) {
150
- afLogger.trace(`🪲pnpm ${command} errors/warnings: ${err}`);
181
+ afLogger.trace(`🪲${packageExecutable} ${command} errors/warnings: ${err}`);
151
182
  }
152
183
  }
153
184
  async rmTmpDir() {
@@ -187,7 +218,7 @@ class CodeInjector {
187
218
  npmLock = JSON.parse(await fs.promises.readFile(npmLockPath, 'utf-8'));
188
219
  }
189
220
  catch (npmLockError) {
190
- throw new Error(`Custom pnpm-lock.yaml does not exist in ${dir}, but package.json does.
221
+ throw new Error(`Custom pnpm-lock.yaml or package-lock.json does not exist in ${dir}, but package.json does.
191
222
  We can't determine version of packages without pnpm-lock.yaml or package-lock.json. Please run pnpm install or npm install in ${dir}`);
192
223
  }
193
224
  lockHash = hashify(npmLock);
@@ -583,6 +614,7 @@ class CodeInjector {
583
614
  routerVueContent = routerVueContent.replace('/* IMPORTANT:ADMINFORTH ROUTES */', routes);
584
615
  await fs.promises.writeFile(routerVuePath, routerVueContent);
585
616
  /* hash checking */
617
+ // Here we use pnpm-lock.yaml, because this file will be anywat, even if user doesn't use it
586
618
  const spaPnpmLockPath = path.join(this.spaTmpPath(), 'pnpm-lock.yaml');
587
619
  const spaPnpmLock = yaml.parse(await fs.promises.readFile(spaPnpmLockPath, 'utf-8'));
588
620
  const spaLockHash = hashify(spaPnpmLock);
@@ -625,7 +657,8 @@ class CodeInjector {
625
657
  // ignore
626
658
  afLogger.trace(`🪲Hash file does not exist, proceeding with pnpm install, ${e}`);
627
659
  }
628
- await this.runPnpmShell({ command: 'install --frozen-lockfile', cwd: this.spaTmpPath(), envOverrides: {
660
+ // install --frozen-lockfile works for npm and pnpm
661
+ await this.runPackageManagerShell({ command: 'install --frozen-lockfile', cwd: this.spaTmpPath(), envOverrides: {
629
662
  NODE_ENV: 'development' // otherwise it will not install devDependencies which we still need, e.g for extract
630
663
  } });
631
664
  const allPacks = [
@@ -642,9 +675,9 @@ class CodeInjector {
642
675
  });
643
676
  const allPacksUnique = Array.from(new Set(allPacksFiltered));
644
677
  if (allPacks.length) {
645
- const pnpmInstallCommand = `install ${allPacksUnique.join(' ')}`;
646
- await this.runPnpmShell({
647
- command: pnpmInstallCommand, cwd: this.spaTmpPath(),
678
+ const packageManagerInstallCommand = `install ${allPacksUnique.join(' ')}`;
679
+ await this.runPackageManagerShell({
680
+ command: packageManagerInstallCommand, cwd: this.spaTmpPath(),
648
681
  envOverrides: {
649
682
  NODE_ENV: 'development' // otherwise it will not install devDependencies which we still need, e.g for extract
650
683
  }
@@ -818,7 +851,7 @@ class CodeInjector {
818
851
  await fs.promises.mkdir(serveDir, { recursive: true });
819
852
  }
820
853
  if (!skipExtract) {
821
- await this.runPnpmShell({ command: 'run i18n:extract', cwd });
854
+ await this.runPackageManagerShell({ command: 'run i18n:extract', cwd });
822
855
  // create serveDir if not exists
823
856
  await fs.promises.mkdir(serveDir, { recursive: true });
824
857
  // copy i18n messages to serve dir
@@ -832,7 +865,7 @@ class CodeInjector {
832
865
  if (!hotReload) {
833
866
  if (!skipBuild) {
834
867
  // TODO probably add option to build with tsh check (plain 'build')
835
- await this.runPnpmShell({ command: 'run build-only', cwd });
868
+ await this.runPackageManagerShell({ command: 'run build-only', cwd });
836
869
  // coy dist to serveDir
837
870
  await fsExtra.copy(path.join(cwd, 'dist'), serveDir, { recursive: true });
838
871
  // save hash
@@ -844,19 +877,20 @@ class CodeInjector {
844
877
  }
845
878
  else {
846
879
  const command = 'run dev';
847
- afLogger.info(`⚙️ spawn: pnpm ${command}...`);
880
+ const usersPackageManager = await this.doesUserHasPnpmLockFile(this.adminforth.config.customization.customComponentsDir) ? 'pnpm' : 'npm';
881
+ afLogger.info(`⚙️ spawn: ${usersPackageManager} ${command}...`);
848
882
  if (process.env.VITE_ADMINFORTH_PUBLIC_PATH) {
849
883
  afLogger.info(`⚠️ Your VITE_ADMINFORTH_PUBLIC_PATH: ${process.env.VITE_ADMINFORTH_PUBLIC_PATH} has no effect`);
850
884
  }
851
885
  const env = Object.assign({ VITE_ADMINFORTH_PUBLIC_PATH: this.adminforth.config.baseUrl, FORCE_COLOR: '1' }, process.env);
852
886
  const nodeBinary = process.execPath;
853
- const pnpmPath = path.join(path.dirname(nodeBinary), 'pnpm');
887
+ const packageManagerPath = path.join(path.dirname(nodeBinary), usersPackageManager);
854
888
  let devServer;
855
889
  if (process.platform === 'win32') {
856
- devServer = spawn('pnpm', command.split(' '), { cwd, env, shell: true });
890
+ devServer = spawn(usersPackageManager, command.split(' '), { cwd, env, shell: true });
857
891
  }
858
892
  else {
859
- devServer = spawn(`${nodeBinary}`, [`${pnpmPath}`, ...command.split(' ')], { cwd, env });
893
+ devServer = spawn(`${nodeBinary}`, [`${packageManagerPath}`, ...command.split(' ')], { cwd, env });
860
894
  }
861
895
  devServer.stdout.on('data', (data) => {
862
896
  if (data.includes('➜')) {