@inizioevoke/veeva-astroclm-core 1.4.0 → 1.4.2

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.
@@ -44,6 +44,7 @@ export default function ({ veevaEnv = 'client', isTraining = false }: IVeevaConf
44
44
 
45
45
  shared: {
46
46
  externalId: formatExtId('SHARED'),
47
+ // zipFileName: 'shared.zip',
47
48
  ...binderFields
48
49
  },
49
50
 
@@ -1,5 +1,7 @@
1
+ import { IVeevaConfig } from '../types';
1
2
  interface Params {
3
+ config: IVeevaConfig;
2
4
  deployDir: string;
3
5
  }
4
- export default function ({ deployDir }: Params): Promise<void>;
6
+ export default function ({ config, deployDir }: Params): Promise<void>;
5
7
  export {};
@@ -3,14 +3,18 @@ import { join } from 'node:path';
3
3
  import Zip from 'adm-zip';
4
4
  import logger from '../lib/logger.js';
5
5
  import { pathToSlideZip } from '../lib/utils.js';
6
- export default async function ({ deployDir }) {
6
+ export default async function ({ config, deployDir }) {
7
7
  logger.info('Creating ZIP files');
8
8
  const dirs = await readdir(deployDir);
9
9
  for (const dir of dirs) {
10
10
  const dirPath = join(deployDir, dir);
11
11
  const zip = new Zip();
12
12
  zip.addLocalFolder(dirPath);
13
- await zip.writeZipPromise(join(deployDir, pathToSlideZip(dir)));
13
+ await zip.writeZipPromise(join(deployDir, getZipFileName(config, dir)));
14
14
  await rm(dirPath, { recursive: true });
15
15
  }
16
16
  }
17
+ function getZipFileName(config, dir) {
18
+ const zipFileName = dir === 'shared' ? config.presentation.shared?.zipFileName : config.presentation.slides.find(s => s.zipFileName === dir)?.zipFileName;
19
+ return pathToSlideZip(zipFileName ?? dir);
20
+ }
@@ -1,7 +1,9 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { mkdir, rm, readdir } from 'node:fs/promises';
3
3
  import { join } from "node:path";
4
+ import { fileURLToPath } from 'node:url';
4
5
  import { preview } from "astro";
6
+ import { validateConfig } from "astro/config";
5
7
  import parseArgv from '../lib/parse-argv.js';
6
8
  import copyFiles from './copy-files.js';
7
9
  import generateThumbs from './generate-thumbs.js';
@@ -15,10 +17,12 @@ const argv = parseArgv({
15
17
  export default async function ({ root, config, env, csvOnly = false, serverPort = 4320 }) {
16
18
  const rootDir = root ?? process.cwd();
17
19
  // we could import the astro config and see if the outDir was set but right now it's not necessary
18
- const astroDistDir = join(rootDir, 'dist');
20
+ const astroConfig = await getAstroConfig(root);
21
+ const outDir = astroConfig ? fileURLToPath(astroConfig.outDir).replace(root, '') : 'dist';
22
+ const astroDistDir = join(rootDir, outDir);
19
23
  const buildName = `${env}${config.presentation.isTraining === true ? '-training' : ''}`;
20
24
  const buildType = argv.mode ?? buildName;
21
- const deployDir = join(rootDir, 'deploy', buildType);
25
+ const deployDir = join(rootDir, outDir !== 'deploy' ? 'deploy' : 'deploy-veeva', buildType);
22
26
  if (existsSync(deployDir)) {
23
27
  const items = await readdir(deployDir);
24
28
  for (const item of items) {
@@ -44,7 +48,28 @@ export default async function ({ root, config, env, csvOnly = false, serverPort
44
48
  root
45
49
  });
46
50
  await server.stop();
47
- await createZips({ deployDir });
51
+ await createZips({ config, deployDir });
48
52
  }
49
53
  await createCsv({ config, deployDir, buildName, fieldsOnly: csvOnly });
50
54
  }
55
+ async function getAstroConfig(root) {
56
+ let config = undefined;
57
+ try {
58
+ config = await validateConfig({}, root, 'build');
59
+ }
60
+ catch (err) {
61
+ console.error('Failed to parse Astro config');
62
+ }
63
+ return config;
64
+ // console.log('Searching for Astro config');
65
+ // const files = await readdir(root, { withFileTypes: true });
66
+ // const configFile = files.find((f) => {
67
+ // return f.isFile() || /^astro\.config\.(?:js|cjs|mjs|ts)$/i.test(f.name);
68
+ // });
69
+ // if (!configFile) {
70
+ // throw new Error('Could not locate Astro config file');
71
+ // }
72
+ // const { default: astroConfig } = await import(pathToFileURL(join(root, configFile.name)).toString());
73
+ // console.log(astroConfig);
74
+ // return astroConfig;
75
+ }
@@ -2,6 +2,7 @@ import { writeFile } from "node:fs/promises";
2
2
  import { extname, join } from "node:path";
3
3
  import puppeteer from "puppeteer";
4
4
  import sharp from 'sharp';
5
+ import { pdf } from "pdf-to-img";
5
6
  import logger from '../lib/logger.js';
6
7
  export default async function ({ config, root, deployDir, server }) {
7
8
  logger.info('Generating thumbnails');
@@ -18,25 +19,41 @@ export default async function ({ config, root, deployDir, server }) {
18
19
  });
19
20
  try {
20
21
  for (const slide of config.presentation.slides) {
22
+ if (slide.thumbnails === false)
23
+ continue;
24
+ // this shouldn't ever be the case after initThumbSettings runs but it keeps typescript happy
21
25
  if (!slide.thumbnails || !slide.thumbnails.settings)
22
26
  continue;
23
27
  for (const settings of slide.thumbnails.settings) {
24
28
  const settingsPath = settings.path ?? slide.path;
25
29
  const urlPath = `${settingsPath.startsWith('/') ? '' : '/'}${settingsPath}${extname(settingsPath) !== '' ? '' : '/'}`;
26
30
  logger.info(`Generating ${urlPath}`);
27
- const url = `${server.protocol ?? 'http'}://${server.host}:${server.port}${urlPath}${settings.queryStringParams ? `?${objToQS(settings.queryStringParams)}` : ''}`;
28
- await page.goto(url, {
29
- waitUntil: 'networkidle0'
30
- });
31
- if (settings.delay && settings.delay > 0) {
32
- logger.info(`Delaying thumbnail generation for ${settings.delay}ms`);
33
- await sleep(settings.delay);
31
+ let screenshot;
32
+ switch (slide.mediaType) {
33
+ case 'PDF': {
34
+ await using document = await pdf(join(deployDir, settingsPath), { scale: 2 });
35
+ screenshot = await document.getPage(1);
36
+ break;
37
+ }
38
+ case 'HTML':
39
+ default: {
40
+ const url = `${server.protocol ?? 'http'}://${server.host}:${server.port}${urlPath}${settings.queryStringParams ? `?${objToQS(settings.queryStringParams)}` : ''}`;
41
+ await page.goto(url, {
42
+ waitUntil: 'networkidle0'
43
+ });
44
+ if (settings.delay && settings.delay > 0) {
45
+ logger.info(`Delaying thumbnail generation for ${settings.delay}ms`);
46
+ await sleep(settings.delay);
47
+ }
48
+ screenshot = await page.screenshot();
49
+ break;
50
+ }
34
51
  }
35
- let screenshot = await page.screenshot();
36
52
  let thumb = await (sharp(screenshot)
37
53
  .resize({
38
54
  width: settings.width,
39
- height: settings.height
55
+ height: settings.height,
56
+ fit: 'inside'
40
57
  })
41
58
  .png({
42
59
  palette: settings.palette ?? true,
@@ -206,6 +223,9 @@ function initThumbSettings(config) {
206
223
  }
207
224
  }
208
225
  }
226
+ for (let i = 0; i < slide.thumbnails.settings.length; i++) {
227
+ slide.thumbnails.settings[i].width ??= config.thumbnails[0]?.width ?? DEFAULT_WIDTH;
228
+ }
209
229
  }
210
230
  }
211
231
  function sleep(timeout = 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inizioevoke/veeva-astroclm-core",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "",
5
5
  "license": "ISC",
6
6
  "author": "",
@@ -33,18 +33,25 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@clack/prompts": "^1.4.0",
36
- "adm-zip": "^0.5.17",
37
- "puppeteer": "^24.43.1",
38
- "sharp": "^0.34.5",
39
- "yaml": "^2.9.0"
36
+ "adm-zip": "^0.6.1",
37
+ "pdf-to-img": "^7.0.0",
38
+ "puppeteer": "^25.13.0",
39
+ "sharp": "^0.35.5",
40
+ "yaml": "^2.9.1"
40
41
  },
41
42
  "devDependencies": {
42
- "@astrojs/check": "^0.9.9",
43
- "@astrojs/ts-plugin": "^1.10.7",
43
+ "@astrojs/check": "^0.9.10",
44
+ "@astrojs/ts-plugin": "^1.10.12",
44
45
  "@types/adm-zip": "^0.5.8",
45
- "@types/node": "^25.7.0",
46
- "astro": "^6.3.1",
47
- "tsx": "^4.21.0",
46
+ "@types/node": "^26.6.4",
47
+ "astro": "^7.3.8",
48
+ "tsx": "^4.23.15",
48
49
  "typescript": "^6.0.3"
50
+ },
51
+ "allowScripts": {
52
+ "sharp": true,
53
+ "esbuild": true,
54
+ "fsevents": true,
55
+ "puppeteer": true
49
56
  }
50
57
  }
@@ -44,6 +44,7 @@ export default function ({ veevaEnv = 'client', isTraining = false }: IVeevaConf
44
44
 
45
45
  shared: {
46
46
  externalId: formatExtId('SHARED'),
47
+ // zipFileName: 'shared.zip',
47
48
  ...binderFields
48
49
  },
49
50
 
@@ -3,11 +3,13 @@ import { join } from 'node:path';
3
3
  import Zip from 'adm-zip';
4
4
  import logger from '../lib/logger.js';
5
5
  import { pathToSlideZip } from '../lib/utils.js';
6
+ import { IVeevaConfig } from '../types';
6
7
 
7
8
  interface Params {
9
+ config: IVeevaConfig;
8
10
  deployDir: string;
9
11
  }
10
- export default async function({ deployDir }: Params) {
12
+ export default async function({ config, deployDir }: Params) {
11
13
  logger.info('Creating ZIP files');
12
14
  const dirs = await readdir(deployDir);
13
15
 
@@ -15,7 +17,13 @@ export default async function({ deployDir }: Params) {
15
17
  const dirPath = join(deployDir, dir);
16
18
  const zip = new Zip();
17
19
  zip.addLocalFolder(dirPath);
18
- await zip.writeZipPromise(join(deployDir, pathToSlideZip(dir)));
20
+ await zip.writeZipPromise(join(deployDir, getZipFileName(config, dir)));
19
21
  await rm(dirPath, { recursive: true });
20
22
  }
23
+ }
24
+
25
+ function getZipFileName(config: IVeevaConfig, dir: string) {
26
+ const zipFileName = dir === 'shared' ? config.presentation.shared?.zipFileName : config.presentation.slides.find(s => s.zipFileName === dir)?.zipFileName;
27
+
28
+ return pathToSlideZip(zipFileName ?? dir);
21
29
  }
@@ -1,7 +1,9 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { mkdir, rm, readdir } from 'node:fs/promises';
3
- import { join, resolve as resolvePath } from "node:path"
4
- import { preview } from "astro";
3
+ import { join, resolve as resolvePath } from "node:path";
4
+ import { pathToFileURL, fileURLToPath } from 'node:url';
5
+ import { AstroConfig, preview } from "astro";
6
+ import { validateConfig } from "astro/config";
5
7
  import { IVeevaConfig } from '../types';
6
8
  import loadEnv from '../lib/env.js';
7
9
  import logger from '../lib/logger.js';
@@ -29,10 +31,12 @@ interface Opts {
29
31
  export default async function({ root, config, env, csvOnly = false, serverPort = 4320 }: Opts) {
30
32
  const rootDir = root ?? process.cwd();
31
33
  // we could import the astro config and see if the outDir was set but right now it's not necessary
32
- const astroDistDir = join(rootDir, 'dist');
34
+ const astroConfig = await getAstroConfig(root);
35
+ const outDir = astroConfig ? fileURLToPath(astroConfig.outDir).replace(root, '') : 'dist';
36
+ const astroDistDir = join(rootDir, outDir);
33
37
  const buildName = `${env}${config.presentation.isTraining === true ? '-training' : ''}`;
34
38
  const buildType = argv.mode ?? buildName;
35
- const deployDir = join(rootDir, 'deploy', buildType);
39
+ const deployDir = join(rootDir, outDir !== 'deploy' ? 'deploy' : 'deploy-veeva', buildType);
36
40
 
37
41
  if (existsSync(deployDir)) {
38
42
  const items = await readdir(deployDir);
@@ -63,11 +67,31 @@ export default async function({ root, config, env, csvOnly = false, serverPort =
63
67
 
64
68
  await server.stop();
65
69
 
66
- await createZips({ deployDir });
70
+ await createZips({ config, deployDir });
67
71
  }
68
72
 
69
73
  await createCsv({ config, deployDir, buildName, fieldsOnly: csvOnly })
70
74
  }
71
75
 
72
76
 
77
+ async function getAstroConfig(root: string) {
78
+ let config: AstroConfig | undefined = undefined;
79
+ try {
80
+ config = await validateConfig({}, root, 'build');
81
+ } catch (err) {
82
+ console.error('Failed to parse Astro config')
83
+ }
84
+ return config;
85
+ // console.log('Searching for Astro config');
86
+ // const files = await readdir(root, { withFileTypes: true });
87
+ // const configFile = files.find((f) => {
88
+ // return f.isFile() || /^astro\.config\.(?:js|cjs|mjs|ts)$/i.test(f.name);
89
+ // });
90
+ // if (!configFile) {
91
+ // throw new Error('Could not locate Astro config file');
92
+ // }
93
+ // const { default: astroConfig } = await import(pathToFileURL(join(root, configFile.name)).toString());
94
+ // console.log(astroConfig);
95
+ // return astroConfig;
96
+ }
73
97
 
@@ -2,6 +2,7 @@ import { writeFile } from "node:fs/promises";
2
2
  import { extname, join } from "node:path";
3
3
  import puppeteer, { type Viewport } from "puppeteer";
4
4
  import sharp from 'sharp';
5
+ import { pdf } from "pdf-to-img";
5
6
 
6
7
  import logger from '../lib/logger.js';
7
8
  import type { IVeevaConfig, IThumbnailSettings, IScreenshotSettings } from "../types";
@@ -34,27 +35,48 @@ export default async function({ config, root, deployDir, server }: Params) {
34
35
 
35
36
  try {
36
37
  for (const slide of config.presentation.slides) {
37
- if (!slide.thumbnails || !slide.thumbnails.settings) continue;
38
+ if (slide.thumbnails === false) continue;
39
+
40
+ // this shouldn't ever be the case after initThumbSettings runs but it keeps typescript happy
41
+ if (!slide.thumbnails || !slide.thumbnails.settings) continue;
42
+
43
+
44
+
38
45
 
39
46
  for (const settings of slide.thumbnails.settings) {
40
47
  const settingsPath = settings.path ?? slide.path;
41
48
  const urlPath = `${settingsPath.startsWith('/') ? '' : '/'}${settingsPath}${extname(settingsPath) !== '' ? '' : '/'}`;
42
49
  logger.info(`Generating ${urlPath}`);
43
- const url = `${server.protocol ?? 'http'}://${server.host}:${server.port}${urlPath}${settings.queryStringParams ? `?${objToQS(settings.queryStringParams)}` : ''}`
44
50
 
45
- await page.goto(url, {
46
- waitUntil: 'networkidle0'
47
- });
48
- if (settings.delay && settings.delay > 0) {
49
- logger.info(`Delaying thumbnail generation for ${settings.delay}ms`);
50
- await sleep(settings.delay);
51
+ let screenshot: Buffer | Uint8Array;
52
+ switch (slide.mediaType) {
53
+ case 'PDF': {
54
+ await using document = await pdf(join(deployDir, settingsPath), { scale: 2 });
55
+ screenshot = await document.getPage(1);
56
+ break;
57
+ }
58
+ case 'HTML':
59
+ default: {
60
+ const url = `${server.protocol ?? 'http'}://${server.host}:${server.port}${urlPath}${settings.queryStringParams ? `?${objToQS(settings.queryStringParams)}` : ''}`
61
+
62
+ await page.goto(url, {
63
+ waitUntil: 'networkidle0'
64
+ });
65
+ if (settings.delay && settings.delay > 0) {
66
+ logger.info(`Delaying thumbnail generation for ${settings.delay}ms`);
67
+ await sleep(settings.delay);
68
+ }
69
+ screenshot = await page.screenshot();
70
+ break;
71
+ }
51
72
  }
52
- let screenshot = await page.screenshot();
73
+
53
74
  let thumb = await (
54
75
  sharp(screenshot)
55
76
  .resize({
56
77
  width: settings.width,
57
- height: settings.height
78
+ height: settings.height,
79
+ fit: 'inside'
58
80
  })
59
81
  .png({
60
82
  palette: settings.palette ?? true,
@@ -230,6 +252,10 @@ function initThumbSettings(config: IVeevaConfig) {
230
252
  }
231
253
  }
232
254
  }
255
+ for (let i=0; i<slide.thumbnails.settings.length; i++) {
256
+ slide.thumbnails.settings[i].width ??= config.thumbnails[0]?.width ?? DEFAULT_WIDTH;
257
+
258
+ }
233
259
  }
234
260
 
235
261
  }