@inizioevoke/veeva-astroclm-core 1.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/.editorconfig +12 -0
- package/build.ts +38 -0
- package/dist/apps/index.d.ts +2 -0
- package/dist/apps/index.js +2 -0
- package/dist/apps/page-manager/bulk-create.d.ts +1 -0
- package/dist/apps/page-manager/bulk-create.js +120 -0
- package/dist/apps/page-manager/index.d.ts +5 -0
- package/dist/apps/page-manager/index.js +24 -0
- package/dist/apps/page-manager/single-create.d.ts +1 -0
- package/dist/apps/page-manager/single-create.js +78 -0
- package/dist/apps/page-manager/templates/contents.astro.txt +12 -0
- package/dist/apps/page-manager/templates/page.astro.txt +4 -0
- package/dist/apps/page-manager/utils.d.ts +10 -0
- package/dist/apps/page-manager/utils.js +57 -0
- package/dist/apps/utils.d.ts +5 -0
- package/dist/apps/utils.js +43 -0
- package/dist/apps/veeva-config-manager/create.d.ts +1 -0
- package/dist/apps/veeva-config-manager/create.js +136 -0
- package/dist/apps/veeva-config-manager/index.d.ts +3 -0
- package/dist/apps/veeva-config-manager/index.js +16 -0
- package/dist/apps/veeva-config-manager/templates/config.ts.txt +60 -0
- package/dist/env/schema.d.ts +15 -0
- package/dist/env/schema.js +28 -0
- package/dist/lib/const.d.ts +15 -0
- package/dist/lib/const.js +14 -0
- package/dist/lib/env.d.ts +8 -0
- package/dist/lib/env.js +17 -0
- package/dist/lib/index.d.ts +5 -0
- package/dist/lib/index.js +5 -0
- package/dist/lib/logger.d.ts +9 -0
- package/dist/lib/logger.js +72 -0
- package/dist/lib/parse-argv.d.ts +31 -0
- package/dist/lib/parse-argv.js +109 -0
- package/dist/lib/parse-env.d.ts +45 -0
- package/dist/lib/parse-env.js +124 -0
- package/dist/lib/utils.d.ts +8 -0
- package/dist/lib/utils.js +37 -0
- package/dist/tasks/copy-files.d.ts +9 -0
- package/dist/tasks/copy-files.js +15 -0
- package/dist/tasks/create-csv.d.ts +15 -0
- package/dist/tasks/create-csv.js +186 -0
- package/dist/tasks/create-zips.d.ts +5 -0
- package/dist/tasks/create-zips.js +16 -0
- package/dist/tasks/deploy.d.ts +10 -0
- package/dist/tasks/deploy.js +49 -0
- package/dist/tasks/generate-thumbs.d.ts +12 -0
- package/dist/tasks/generate-thumbs.js +152 -0
- package/dist/tasks/index.d.ts +1 -0
- package/dist/tasks/index.js +1 -0
- package/dist/tasks/update-shared.d.ts +5 -0
- package/dist/tasks/update-shared.js +41 -0
- package/dist/types/config.d.ts +52 -0
- package/dist/types/env.d.ts +7 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/veeva.d.ts +58 -0
- package/dist/veeva/slideManager.d.ts +33 -0
- package/dist/veeva/slideManager.js +120 -0
- package/package.json +39 -0
- package/src/apps/index.ts +2 -0
- package/src/apps/page-manager/bulk-create.ts +131 -0
- package/src/apps/page-manager/index.ts +31 -0
- package/src/apps/page-manager/single-create.ts +97 -0
- package/src/apps/page-manager/templates/contents.astro.txt +12 -0
- package/src/apps/page-manager/templates/page.astro.txt +4 -0
- package/src/apps/page-manager/utils.ts +70 -0
- package/src/apps/utils.ts +47 -0
- package/src/apps/veeva-config-manager/create.ts +153 -0
- package/src/apps/veeva-config-manager/index.ts +20 -0
- package/src/apps/veeva-config-manager/templates/config.ts.txt +60 -0
- package/src/env/schema.ts +43 -0
- package/src/lib/const.ts +17 -0
- package/src/lib/env.ts +27 -0
- package/src/lib/index.ts +5 -0
- package/src/lib/logger.ts +84 -0
- package/src/lib/parse-argv.ts +125 -0
- package/src/lib/parse-env.ts +147 -0
- package/src/lib/utils.ts +37 -0
- package/src/tasks/copy-files.ts +29 -0
- package/src/tasks/create-csv.ts +259 -0
- package/src/tasks/create-zips.ts +21 -0
- package/src/tasks/deploy.ts +72 -0
- package/src/tasks/generate-thumbs.ts +179 -0
- package/src/tasks/index.ts +1 -0
- package/src/tasks/update-shared.ts +49 -0
- package/src/types/config.d.ts +52 -0
- package/src/types/env.d.ts +7 -0
- package/src/types/index.d.ts +9 -0
- package/src/types/veeva.d.ts +58 -0
- package/src/veeva/readme.md +77 -0
- package/src/veeva/slideManager.ts +139 -0
- package/tsconfig.json +27 -0
package/.editorconfig
ADDED
package/build.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { cp, readdir, rm } from 'node:fs/promises';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import parseArgv from './src/lib/parse-argv';
|
|
5
|
+
|
|
6
|
+
async function clean() {
|
|
7
|
+
const dist = join(import.meta.dirname, 'dist');
|
|
8
|
+
if (existsSync(dist)) {
|
|
9
|
+
const files = await readdir(dist);
|
|
10
|
+
|
|
11
|
+
await Promise.all(
|
|
12
|
+
files.map((f) => {
|
|
13
|
+
return rm(join(dist, f), { recursive: true, force: true });
|
|
14
|
+
})
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const argv = parseArgv({
|
|
20
|
+
'cmd': ['pre', 'post'] as const
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
switch (argv.cmd) {
|
|
24
|
+
case 'pre':
|
|
25
|
+
await clean();
|
|
26
|
+
break;
|
|
27
|
+
case 'post':
|
|
28
|
+
const dirsToCopy = [
|
|
29
|
+
'apps/page-manager/templates',
|
|
30
|
+
'apps/veeva-config-manager/templates',
|
|
31
|
+
'types'
|
|
32
|
+
];
|
|
33
|
+
for (const dir of dirsToCopy) {
|
|
34
|
+
await cp(join(import.meta.dirname, 'src', dir), join(import.meta.dirname, 'dist', dir), { recursive: true });
|
|
35
|
+
}
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function bulkCreate(rootDir: string): Promise<void>;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { readFile, rm } from 'node:fs/promises';
|
|
3
|
+
import { join, resolve as resolvePath } from 'node:path';
|
|
4
|
+
import { parse } from 'yaml';
|
|
5
|
+
import { text, select, spinner, outro, box } from '@clack/prompts';
|
|
6
|
+
import { sleep, handleCanceled } from '../utils.js';
|
|
7
|
+
import { createPageAssets, getLayouts } from './utils.js';
|
|
8
|
+
const __dirname = import.meta.dirname;
|
|
9
|
+
export default async function bulkCreate(rootDir) {
|
|
10
|
+
const contentDir = join(rootDir, 'src/contents');
|
|
11
|
+
const pagesDir = join(rootDir, 'src/pages');
|
|
12
|
+
const bulkPath = handleCanceled(await text({
|
|
13
|
+
message: 'Enter the path, relative to the project root, of the YAML file containing the content to create',
|
|
14
|
+
initialValue: '_contents.yml',
|
|
15
|
+
validate: (value) => {
|
|
16
|
+
if (!value?.trim())
|
|
17
|
+
return 'The path is required';
|
|
18
|
+
if (!existsSync(resolvePath(rootDir, value.trim())))
|
|
19
|
+
return 'The file does not exist';
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
}));
|
|
23
|
+
const layouts = await getLayouts(rootDir);
|
|
24
|
+
const layout = handleCanceled(await select({
|
|
25
|
+
message: 'Select the layout each page should use',
|
|
26
|
+
options: (() => {
|
|
27
|
+
return layouts.map((layout) => {
|
|
28
|
+
const label = layout.replace(/\\/g, '/');
|
|
29
|
+
return {
|
|
30
|
+
label,
|
|
31
|
+
value: label
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
})()
|
|
35
|
+
}));
|
|
36
|
+
const created = [];
|
|
37
|
+
const existing = [];
|
|
38
|
+
const errors = [];
|
|
39
|
+
const spin = spinner();
|
|
40
|
+
spin.start('Generating page assets...');
|
|
41
|
+
await sleep();
|
|
42
|
+
const data = parse(await readFile(resolvePath(rootDir, bulkPath), 'utf8'))
|
|
43
|
+
.map((d) => { return d.trim(); });
|
|
44
|
+
for (const contentPath of data) {
|
|
45
|
+
if (existsSync(join(contentDir, contentPath)) && existsSync(join(pagesDir, `${contentPath.replace(/\//g, '-')}.astro`))) {
|
|
46
|
+
existing.push(contentPath);
|
|
47
|
+
spin.message(`Exists: ${contentPath}`);
|
|
48
|
+
await sleep();
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
try {
|
|
52
|
+
await createPageAssets({ rootDir, contentPath, layout });
|
|
53
|
+
created.push(contentPath);
|
|
54
|
+
spin.message(`Generated: ${contentPath}`);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
errors.push(contentPath);
|
|
58
|
+
spin.message(`Error: ${contentPath}`);
|
|
59
|
+
}
|
|
60
|
+
await sleep();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
spin.stop('Page assets generated!');
|
|
64
|
+
const result = [];
|
|
65
|
+
let insertNewLine = false;
|
|
66
|
+
if (created.length) {
|
|
67
|
+
result.push('The following content/pages were created:');
|
|
68
|
+
created.sort();
|
|
69
|
+
for (const c of created) {
|
|
70
|
+
result.push(` - ${c}`);
|
|
71
|
+
}
|
|
72
|
+
insertNewLine = true;
|
|
73
|
+
}
|
|
74
|
+
if (existing.length) {
|
|
75
|
+
if (insertNewLine) {
|
|
76
|
+
result.push('');
|
|
77
|
+
}
|
|
78
|
+
result.push('The following content/pages already exist:');
|
|
79
|
+
existing.sort();
|
|
80
|
+
for (const c of existing) {
|
|
81
|
+
result.push(` - ${c}`);
|
|
82
|
+
}
|
|
83
|
+
insertNewLine = true;
|
|
84
|
+
}
|
|
85
|
+
if (errors.length) {
|
|
86
|
+
if (insertNewLine) {
|
|
87
|
+
result.push('');
|
|
88
|
+
}
|
|
89
|
+
result.push('The following content/pages experienced an error while creating:');
|
|
90
|
+
errors.sort();
|
|
91
|
+
for (const c of errors) {
|
|
92
|
+
result.push(` - ${c}`);
|
|
93
|
+
}
|
|
94
|
+
insertNewLine = true;
|
|
95
|
+
}
|
|
96
|
+
box(result.join('\n'));
|
|
97
|
+
const deleteFile = handleCanceled(await select({
|
|
98
|
+
message: `Would you like to delete ${bulkPath}?`,
|
|
99
|
+
options: [{
|
|
100
|
+
label: 'Yes, delete the file',
|
|
101
|
+
value: true
|
|
102
|
+
}, {
|
|
103
|
+
label: 'No, keep the file',
|
|
104
|
+
value: false
|
|
105
|
+
}]
|
|
106
|
+
}));
|
|
107
|
+
if (deleteFile) {
|
|
108
|
+
const deleteSpinner = spinner();
|
|
109
|
+
deleteSpinner.start(`Deleting ${bulkPath}`);
|
|
110
|
+
await sleep();
|
|
111
|
+
try {
|
|
112
|
+
await rm(resolvePath(rootDir, bulkPath), { force: true });
|
|
113
|
+
deleteSpinner.stop('File deleted');
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
deleteSpinner.error('An error occurred while attempting to delete the file');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
outro('Happy coding!');
|
|
120
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { intro, select } from '@clack/prompts';
|
|
2
|
+
import { handleCanceled } from '../utils.js';
|
|
3
|
+
export default async function ({ root } = {}) {
|
|
4
|
+
const rootDir = root ?? process.cwd();
|
|
5
|
+
intro('Page Manager');
|
|
6
|
+
const action = handleCanceled(await select({
|
|
7
|
+
message: 'How would you like to create pages?',
|
|
8
|
+
options: [{
|
|
9
|
+
label: 'One at a time',
|
|
10
|
+
value: 'single'
|
|
11
|
+
}, {
|
|
12
|
+
label: 'Bulk create',
|
|
13
|
+
value: 'bulk'
|
|
14
|
+
}]
|
|
15
|
+
}));
|
|
16
|
+
if (action === 'bulk') {
|
|
17
|
+
const bulkCreate = await import('./bulk-create.js');
|
|
18
|
+
await bulkCreate.default(rootDir);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const singleCreate = await import('./single-create.js');
|
|
22
|
+
await singleCreate.default(rootDir);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function singleCreate(rootDir: string): Promise<void>;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { basename, join } from 'node:path';
|
|
3
|
+
import { text, select, note, spinner } from '@clack/prompts';
|
|
4
|
+
import { sleep, handleCanceled } from '../utils.js';
|
|
5
|
+
import { getLayouts, createPageAssets } from './utils.js';
|
|
6
|
+
export default async function singleCreate(rootDir) {
|
|
7
|
+
const contentDir = join(rootDir, 'src/contents');
|
|
8
|
+
const pagesDir = join(rootDir, 'src/pages');
|
|
9
|
+
const layouts = await getLayouts(rootDir);
|
|
10
|
+
note('Page content will be stored in /src/contents\nPage routes will be created in /src/pages');
|
|
11
|
+
const contentPath = handleCanceled(await text({
|
|
12
|
+
message: 'Enter the path for the new page content relative to /src/contents',
|
|
13
|
+
validate: (value) => {
|
|
14
|
+
if (!value?.trim()) {
|
|
15
|
+
return 'The content path is required';
|
|
16
|
+
}
|
|
17
|
+
const contentPath = value.trim();
|
|
18
|
+
if (existsSync(join(contentDir, contentPath))) {
|
|
19
|
+
return 'The content path already exists';
|
|
20
|
+
}
|
|
21
|
+
const pageName = contentPath.replace(/\//g, '-');
|
|
22
|
+
if (existsSync(join(pagesDir, `${pageName}.astro`))) {
|
|
23
|
+
return `The page /src/pages/${pageName}.astro already exists`;
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
}));
|
|
28
|
+
// const name = handleCanceled(await text({
|
|
29
|
+
// message: 'Enter the name of the content file',
|
|
30
|
+
// initialValue: `${basename(path)}.astro`
|
|
31
|
+
// }));
|
|
32
|
+
// const pageName = handleCanceled(await text({
|
|
33
|
+
// message: 'Enter the name of the page',
|
|
34
|
+
// initialValue: `${path.toLowerCase().replace(/\//g, '-')}.astro`
|
|
35
|
+
// }));
|
|
36
|
+
const contentName = `${basename(contentPath)}`;
|
|
37
|
+
const pageName = `${contentPath.toLowerCase().replace(/\//g, '-')}.astro`;
|
|
38
|
+
const layout = handleCanceled(await select({
|
|
39
|
+
message: 'Select the layout the page should use',
|
|
40
|
+
options: (() => {
|
|
41
|
+
return layouts.map((layout) => {
|
|
42
|
+
const label = layout.replace(/\\/g, '/');
|
|
43
|
+
return {
|
|
44
|
+
label,
|
|
45
|
+
value: label
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
})()
|
|
49
|
+
}));
|
|
50
|
+
const spin = spinner();
|
|
51
|
+
spin.start('Generating page assets');
|
|
52
|
+
await sleep();
|
|
53
|
+
await createPageAssets({ rootDir, contentPath, contentName, pageName, layout });
|
|
54
|
+
await sleep();
|
|
55
|
+
spin.stop('Page assets generated!');
|
|
56
|
+
// let path: string | undefined = undefined;
|
|
57
|
+
// do {
|
|
58
|
+
// path = handleCanceled(await text({
|
|
59
|
+
// message: 'Enter the path for the new page content relative to /src/contents',
|
|
60
|
+
// validate: (value: string | undefined) => {
|
|
61
|
+
// if (!value?.trim()) return 'The content path is required';
|
|
62
|
+
// return undefined;
|
|
63
|
+
// }
|
|
64
|
+
// }));
|
|
65
|
+
// const contentPath = path!.trim().toLowerCase();
|
|
66
|
+
// console.log(join(contentDir, contentPath));
|
|
67
|
+
// if (existsSync(join(contentDir, contentPath))) {
|
|
68
|
+
// console.log('The content path already exists');
|
|
69
|
+
// path = undefined;
|
|
70
|
+
// }
|
|
71
|
+
// const pageName = contentPath.replace(/\//g, '-');
|
|
72
|
+
// console.log(join(pagesDir, `${pageName}.astro`));
|
|
73
|
+
// if (existsSync(join(pagesDir, `${pageName}.astro`))) {
|
|
74
|
+
// console.log(`The page /src/pages/${pageName}.astro already exists`);
|
|
75
|
+
// path = undefined;
|
|
76
|
+
// }
|
|
77
|
+
// } while (path === undefined);
|
|
78
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function getLayouts(rootDir: string): Promise<string[]>;
|
|
2
|
+
interface CreateParams {
|
|
3
|
+
rootDir: string;
|
|
4
|
+
contentPath: string;
|
|
5
|
+
contentName?: string;
|
|
6
|
+
pageName?: string;
|
|
7
|
+
layout: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function createPageAssets({ rootDir, contentPath, contentName, pageName, layout }: CreateParams): Promise<void>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { readFile, writeFile, readdir, mkdir } from 'node:fs/promises';
|
|
3
|
+
import { basename, join } from 'node:path';
|
|
4
|
+
export async function getLayouts(rootDir) {
|
|
5
|
+
const layouts = await readdir(join(rootDir, 'src/layouts'), { recursive: true });
|
|
6
|
+
return layouts.filter(l => l.endsWith('.astro')).sort();
|
|
7
|
+
}
|
|
8
|
+
export async function createPageAssets({ rootDir, contentPath, contentName, pageName, layout }) {
|
|
9
|
+
const contentDir = join(rootDir, 'src/contents');
|
|
10
|
+
const pagesDir = join(rootDir, 'src/pages');
|
|
11
|
+
if (!contentName) {
|
|
12
|
+
contentName = basename(contentPath);
|
|
13
|
+
}
|
|
14
|
+
if (!pageName) {
|
|
15
|
+
pageName = `${contentPath
|
|
16
|
+
.split('/')
|
|
17
|
+
.map((p, i, arr) => {
|
|
18
|
+
if (i > 0) {
|
|
19
|
+
let str = p;
|
|
20
|
+
// prevent the page name from having repeated words like "dir-dir-1"
|
|
21
|
+
if (p.startsWith(arr[i - 1]) && p.length > arr[i - 1].length + 1) {
|
|
22
|
+
str = p.substring(arr[i - 1].length);
|
|
23
|
+
if (/[0-9a-z]/i.test(str[0])) {
|
|
24
|
+
return str;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
return str.substring(str.match(/[0-9a-z]/i)?.index ?? 0);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return str;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
return p;
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
.join('-')}.astro`;
|
|
37
|
+
if (existsSync(join(pagesDir, pageName))) {
|
|
38
|
+
pageName = `${contentPath.replace(/\//g, '-')}.astro`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
let pageTemplate = await readFile(join(import.meta.dirname, 'templates/page.astro.txt'), 'utf8');
|
|
42
|
+
pageTemplate = pageTemplate
|
|
43
|
+
.replace(/##CONTENTS_PATH##/g, contentPath)
|
|
44
|
+
.replace(/##CONTENTS_NAME##/g, contentName);
|
|
45
|
+
await writeFile(join(pagesDir, pageName), pageTemplate);
|
|
46
|
+
let contentTemplate = await readFile(join(import.meta.dirname, 'templates/contents.astro.txt'), 'utf8');
|
|
47
|
+
contentTemplate = contentTemplate
|
|
48
|
+
.replace(/##LAYOUT_NAME##/g, basename(layout).replace(/\.astro$/, ''))
|
|
49
|
+
.replace(/##LAYOUT_PATH##/g, layout)
|
|
50
|
+
.replace(/##CONTENT_NAME##/g, contentName);
|
|
51
|
+
if (!existsSync(join(contentDir, contentPath))) {
|
|
52
|
+
await mkdir(join(contentDir, contentPath), { recursive: true });
|
|
53
|
+
}
|
|
54
|
+
await writeFile(join(contentDir, contentPath, `${contentName}.astro`), contentTemplate);
|
|
55
|
+
await writeFile(join(contentDir, contentPath, `${contentName}.scss`), '');
|
|
56
|
+
await writeFile(join(contentDir, contentPath, `${contentName}.ts`), '');
|
|
57
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function sleep(timeout?: number): Promise<unknown>;
|
|
2
|
+
export declare function handleCanceled<T = string>(value: unknown): T;
|
|
3
|
+
export declare function cleanInput(value: string): string;
|
|
4
|
+
export declare const generateRandomId: (length?: number) => string;
|
|
5
|
+
export declare function titleCase(value: string): string;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { getRandomValues } from 'node:crypto';
|
|
2
|
+
import { isCancel, cancel } from '@clack/prompts';
|
|
3
|
+
export function sleep(timeout = 500) {
|
|
4
|
+
return new Promise((resolve) => {
|
|
5
|
+
setTimeout(resolve, timeout);
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
export function handleCanceled(value) {
|
|
9
|
+
if (isCancel(value)) {
|
|
10
|
+
cancel('Canceled');
|
|
11
|
+
process.exit(0);
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function cleanInput(value) {
|
|
18
|
+
return value.trim().replace(/ /g, ' ');
|
|
19
|
+
}
|
|
20
|
+
export const generateRandomId = (() => {
|
|
21
|
+
function chars(count) {
|
|
22
|
+
return [...getRandomValues(new Uint16Array(count))]
|
|
23
|
+
.map((n) => {
|
|
24
|
+
return n.toString(36).toUpperCase();
|
|
25
|
+
})
|
|
26
|
+
.join('');
|
|
27
|
+
}
|
|
28
|
+
return (length = 8) => {
|
|
29
|
+
let id = chars(Math.round(length / 4) + 1);
|
|
30
|
+
while (id.length < length) {
|
|
31
|
+
id += chars(1);
|
|
32
|
+
}
|
|
33
|
+
return id.substring(0, length);
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
export function titleCase(value) {
|
|
37
|
+
return value.split('-')
|
|
38
|
+
.filter(v => v.trim() !== '')
|
|
39
|
+
.map((v) => {
|
|
40
|
+
return `${v[0].toUpperCase()}${v.substring(1)}`;
|
|
41
|
+
})
|
|
42
|
+
.join(' ');
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function (rootDir: string): Promise<void>;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { readFile, writeFile, readdir } from 'node:fs/promises';
|
|
2
|
+
import { join, resolve as resolvePath } from 'node:path';
|
|
3
|
+
import { text, select, multiselect, spinner, outro } from '@clack/prompts';
|
|
4
|
+
import { sleep, handleCanceled, cleanInput, generateRandomId, titleCase } from '../utils.js';
|
|
5
|
+
const __dirname = import.meta.dirname;
|
|
6
|
+
export default async function (rootDir) {
|
|
7
|
+
const product = handleCanceled(await text({
|
|
8
|
+
message: 'Enter the product name',
|
|
9
|
+
validate: (value) => {
|
|
10
|
+
if (!value?.trim())
|
|
11
|
+
return 'Product name is required';
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
}));
|
|
15
|
+
const presentation = handleCanceled(await text({
|
|
16
|
+
message: 'Enter a presentation name',
|
|
17
|
+
validate: (value) => {
|
|
18
|
+
if (!value?.trim())
|
|
19
|
+
return 'Presentation name is required';
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
}));
|
|
23
|
+
const presentationId = handleCanceled(await text({
|
|
24
|
+
message: 'Enter a presentation ID',
|
|
25
|
+
initialValue: presentation.toString().toLowerCase().replace(/[^0-9a-z_]/g, '_'),
|
|
26
|
+
validate: (value) => {
|
|
27
|
+
if (!value?.trim())
|
|
28
|
+
return 'Presentation ID is required';
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}));
|
|
32
|
+
const dimensions = handleCanceled(await select({
|
|
33
|
+
message: 'Select the IVA dimensions',
|
|
34
|
+
options: [{
|
|
35
|
+
label: '1024 x 768',
|
|
36
|
+
value: '1024_768'
|
|
37
|
+
}, {
|
|
38
|
+
label: '1366 x 1024',
|
|
39
|
+
value: '1366_1024'
|
|
40
|
+
}, {
|
|
41
|
+
label: '1376 x 1032',
|
|
42
|
+
value: '1376_1032'
|
|
43
|
+
}],
|
|
44
|
+
initialValue: '1366_1024'
|
|
45
|
+
}));
|
|
46
|
+
const [width, height] = dimensions.split('_');
|
|
47
|
+
const iosResolution = handleCanceled(await select({
|
|
48
|
+
message: 'Select the iOS resolution',
|
|
49
|
+
options: [{
|
|
50
|
+
label: 'Default For Device',
|
|
51
|
+
value: 'Default For Device'
|
|
52
|
+
}, {
|
|
53
|
+
label: 'Scale To Fit',
|
|
54
|
+
value: 'Scale To Fit'
|
|
55
|
+
}, {
|
|
56
|
+
label: 'Scale To 1024x768',
|
|
57
|
+
value: 'Scale To 1024x768'
|
|
58
|
+
}],
|
|
59
|
+
initialValue: 'Default For Device'
|
|
60
|
+
}));
|
|
61
|
+
const disableActions = handleCanceled(await multiselect({
|
|
62
|
+
message: 'Select any actions you would like to disable globally',
|
|
63
|
+
required: false,
|
|
64
|
+
options: [{
|
|
65
|
+
label: 'History Buttons',
|
|
66
|
+
value: 'History Buttons'
|
|
67
|
+
}, {
|
|
68
|
+
label: 'Navigation Bar',
|
|
69
|
+
value: 'Navigation Bar'
|
|
70
|
+
}, {
|
|
71
|
+
label: 'Pinch to Exit',
|
|
72
|
+
value: 'Pinch to Exit'
|
|
73
|
+
}, {
|
|
74
|
+
label: 'Reactions',
|
|
75
|
+
value: 'Reactions'
|
|
76
|
+
}, {
|
|
77
|
+
label: 'Rotation Lock',
|
|
78
|
+
value: 'Rotation Lock'
|
|
79
|
+
}, {
|
|
80
|
+
label: 'Swipe',
|
|
81
|
+
value: 'Swipe'
|
|
82
|
+
}, {
|
|
83
|
+
label: 'Zoom',
|
|
84
|
+
value: 'Zoom'
|
|
85
|
+
}]
|
|
86
|
+
}));
|
|
87
|
+
const crmTarget = handleCanceled(await select({
|
|
88
|
+
message: 'Select the Veeva platform version',
|
|
89
|
+
options: [{
|
|
90
|
+
label: 'Salesforce',
|
|
91
|
+
value: 'Salesforce'
|
|
92
|
+
}, {
|
|
93
|
+
label: 'Vault',
|
|
94
|
+
value: 'Vault'
|
|
95
|
+
}],
|
|
96
|
+
initialValue: 'Salesforce'
|
|
97
|
+
}));
|
|
98
|
+
const spin = spinner();
|
|
99
|
+
spin.start('Generating config file...');
|
|
100
|
+
await sleep(); // make it seem like we're doing something
|
|
101
|
+
const slides = [];
|
|
102
|
+
const slideIds = new Set();
|
|
103
|
+
const pages = await readdir(resolvePath(rootDir, 'src/pages'));
|
|
104
|
+
for (const page of pages) {
|
|
105
|
+
if (page.endsWith('.astro')) {
|
|
106
|
+
const p = page.slice(0, -6);
|
|
107
|
+
let slideId = generateRandomId();
|
|
108
|
+
while (slideIds.has(slideId)) {
|
|
109
|
+
slideId = generateRandomId();
|
|
110
|
+
}
|
|
111
|
+
slideIds.add(slideId);
|
|
112
|
+
slides.push(`{
|
|
113
|
+
path: '${p}',
|
|
114
|
+
title: '${titleCase(p)}',
|
|
115
|
+
externalId: formatExtId('${slideId}')
|
|
116
|
+
}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
let template = await readFile(join(__dirname, 'templates/config.ts.txt'), 'utf8');
|
|
120
|
+
template = template
|
|
121
|
+
.replace(/##EXTERNAL_ID##/g, `${cleanInput(product).substring(0, 3).toUpperCase()}_${generateRandomId()}`)
|
|
122
|
+
.replace(/##PRODUCT##/g, cleanInput(product))
|
|
123
|
+
.replace(/##PRESENTATION_NAME##/g, cleanInput(presentation))
|
|
124
|
+
.replace(/##PRESENTATION_ID##/g, cleanInput(presentationId))
|
|
125
|
+
.replace(/##DIMENSIONS_WIDTH##/g, width)
|
|
126
|
+
.replace(/##DIMENSIONS_HEIGHT##/g, height)
|
|
127
|
+
.replace(/##IOS_RESOLUTION##/g, iosResolution)
|
|
128
|
+
.replace(/##DISABLE_ACTIONS##/g, `[${disableActions.map((a) => { return `'${a}'`; }).join(', ')}]`)
|
|
129
|
+
.replace(/##CRM_TARGET##/g, crmTarget)
|
|
130
|
+
.replace(/##SLIDES##/g, `[${slides.join(', ')}]`)
|
|
131
|
+
.replace(/##SLIDE_IDS##/g, [...slideIds].join());
|
|
132
|
+
await writeFile(resolvePath(rootDir, 'veeva-config.ts'), template);
|
|
133
|
+
await sleep(); // make it seem like we're doing something
|
|
134
|
+
spin.stop('Config file generated!');
|
|
135
|
+
outro('Happy coding!');
|
|
136
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { resolve as resolvePath } from 'node:path';
|
|
3
|
+
import { intro } from '@clack/prompts';
|
|
4
|
+
export default async function ({ root } = {}) {
|
|
5
|
+
const rootDir = root ?? process.cwd();
|
|
6
|
+
const exists = existsSync(resolvePath(rootDir, 'veeva-config.ts'));
|
|
7
|
+
console.log('');
|
|
8
|
+
intro('Veeva Configuration Manager');
|
|
9
|
+
if (!exists) {
|
|
10
|
+
const create = await import('./create.js');
|
|
11
|
+
await create.default(rootDir);
|
|
12
|
+
} /* else {
|
|
13
|
+
const update = await import('../../../archive/update');
|
|
14
|
+
await update.default();
|
|
15
|
+
} */
|
|
16
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { IVeevaClmBinderFields, IVeevaConfig, VeevaEnv } from "@inizioevoke/veeva-astroclm-core/types";
|
|
2
|
+
import { formatExtId as __formatExtId } from '@inizioevoke/veeva-astroclm-core/lib';
|
|
3
|
+
|
|
4
|
+
interface IVeevaConfigArgs {
|
|
5
|
+
veevaEnv?: VeevaEnv;
|
|
6
|
+
isTraining?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export default function ({ veevaEnv = 'client', isTraining = false }: IVeevaConfigArgs = {}): IVeevaConfig {
|
|
9
|
+
const isEvokeBuild = veevaEnv === 'evoke';
|
|
10
|
+
const EXT_ID = `##EXTERNAL_ID##${isTraining ? '_TRN' : ''}`;
|
|
11
|
+
const PRESENTATION_NAME = '##PRESENTATION_NAME##';
|
|
12
|
+
|
|
13
|
+
const formatExtId = (id: string) => {
|
|
14
|
+
return __formatExtId(EXT_ID, id);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const binderFields: IVeevaClmBinderFields = {
|
|
18
|
+
// language: isEvokeBuild ? undefined : 'English',
|
|
19
|
+
// crmOrg: isEvokeBuild ? undefined : { crmId: ['CRM_ORD_ID'] },
|
|
20
|
+
// crmProduct: isEvokeBuild ? undefined : { id: 'VEEVA_ID_FROM_URL' },
|
|
21
|
+
// crmDetailGroup: isEvokeBuild ? undefined : { id: 'VEEVA_ID_FROM_URL' },
|
|
22
|
+
// detailGroup: undefined
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
dimensions: { width: ##DIMENSIONS_WIDTH##, height: ##DIMENSIONS_HEIGHT## },
|
|
27
|
+
thumbnails: {
|
|
28
|
+
default: {
|
|
29
|
+
queryStringParams: {
|
|
30
|
+
screenshots: true
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
presentation: {
|
|
35
|
+
externalId: EXT_ID,
|
|
36
|
+
presentationId: `##PRESENTATION_ID##${isTraining ? '_trn' : ''}`,
|
|
37
|
+
product: '##PRODUCT##',
|
|
38
|
+
name: `${PRESENTATION_NAME}${isTraining ? ' [Training]' : ''}`,
|
|
39
|
+
country: 'United States',
|
|
40
|
+
isTraining,
|
|
41
|
+
crmTarget: '##CRM_TARGET##',
|
|
42
|
+
...binderFields,
|
|
43
|
+
|
|
44
|
+
shared: {
|
|
45
|
+
externalId: formatExtId('SHARED'),
|
|
46
|
+
...binderFields
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
slideSettings: {
|
|
50
|
+
mediaType: 'HTML',
|
|
51
|
+
disableActions: ##DISABLE_ACTIONS##,
|
|
52
|
+
iosResolution: '##IOS_RESOLUTION##',
|
|
53
|
+
...binderFields
|
|
54
|
+
},
|
|
55
|
+
slides: ##SLIDES##
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// SLIDE_IDS: [##SLIDE_IDS##]
|