Refine experience descriptions for clarity and brevity, adjust green theme colors and spacing, add part-time employment type, add company-section-label style, set print media type in export script, and switch background to white. Made-with: Cursor
127 lines
3.7 KiB
JavaScript
Executable File
127 lines
3.7 KiB
JavaScript
Executable File
const puppeteer = require('puppeteer');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const http = require('http');
|
|
const config = require('../config');
|
|
|
|
const devPort = process.env.PORT || config.dev.port;
|
|
|
|
const {
|
|
interval
|
|
} = require('rxjs');
|
|
const {
|
|
filter,
|
|
first,
|
|
mergeMap
|
|
} = require('rxjs/operators');
|
|
|
|
const fetchResponse = () => {
|
|
return new Promise((res, rej) => {
|
|
try {
|
|
const req = http.request(`http://localhost:${devPort}/#/`, response => res(response.statusCode));
|
|
req.on('error', (err) => rej(err));
|
|
req.end();
|
|
} catch (err) {
|
|
rej(err);
|
|
}
|
|
});
|
|
};
|
|
|
|
const waitForServerReachable = () => {
|
|
return interval(1000).pipe(
|
|
mergeMap(async () => {
|
|
try {
|
|
const statusCode = await fetchResponse();
|
|
if (statusCode === 200) return true;
|
|
} catch (err) {}
|
|
return false;
|
|
}),
|
|
filter(ok => !!ok)
|
|
);
|
|
};
|
|
/*
|
|
const timedOut = timeout => {
|
|
return new Promise(res => {
|
|
setTimeout(res, timeout);
|
|
});
|
|
};
|
|
*/
|
|
const convert = async () => {
|
|
await waitForServerReachable().pipe(
|
|
first()
|
|
).toPromise();
|
|
|
|
console.log('Connected to server ...');
|
|
console.log('Exporting ...');
|
|
try {
|
|
const fullDirectoryPath = path.join(__dirname, '../pdf/');
|
|
let directories = getResumesFromDirectories();
|
|
const resumeFilterRaw = (process.env.EXPORT_RESUME || process.argv[2] || '').trim();
|
|
const resumeFilter = resumeFilterRaw.replace(/\.vue$/i, '').toLowerCase();
|
|
if (resumeFilter) {
|
|
directories = directories.filter(d => d.name.toLowerCase() === resumeFilter);
|
|
if (directories.length === 0) {
|
|
console.error(
|
|
`No resume template "${resumeFilterRaw}". Expected a name like "green" (see src/resumes/*.vue).`
|
|
);
|
|
process.exit(1);
|
|
}
|
|
console.log('Resume filter: ' + directories.map(d => d.name).join(', '));
|
|
}
|
|
for (const dir of directories) {
|
|
const browser = await puppeteer.launch({
|
|
headless: true,
|
|
args: ['--no-sandbox', '--font-render-hinting=none']
|
|
});
|
|
const page = await browser.newPage();
|
|
await page.goto(`http://localhost:${devPort}/#/resume/` + dir.name, {
|
|
waitUntil: 'load',
|
|
timeout: 120000
|
|
});
|
|
try {
|
|
await page.evaluate(() => document.fonts.ready);
|
|
} catch (_err) {
|
|
/* ignore if fonts API missing */
|
|
}
|
|
await page.emulateMediaType('print');
|
|
await new Promise((r) => setTimeout(r, 300));
|
|
|
|
if (
|
|
!fs.existsSync(fullDirectoryPath)
|
|
) {
|
|
fs.mkdirSync(fullDirectoryPath);
|
|
}
|
|
await page.pdf({
|
|
path: fullDirectoryPath + dir.name + '.pdf',
|
|
format: 'A4',
|
|
printBackground: true,
|
|
margin: { top: '0', bottom: '0', left: '0', right: '0' }
|
|
});
|
|
await browser.close();
|
|
}
|
|
} catch (err) {
|
|
throw new Error(err);
|
|
}
|
|
console.log('Finished exports.');
|
|
};
|
|
|
|
const getResumesFromDirectories = () => {
|
|
const directories = getDirectories();
|
|
return directories
|
|
.map(dir => {
|
|
const fileName = dir.replace('.vue', '');
|
|
return {
|
|
path: fileName,
|
|
name: fileName
|
|
};
|
|
});
|
|
};
|
|
|
|
const getDirectories = () => {
|
|
const srcpath = path.join(__dirname, '../src/resumes');
|
|
return fs.readdirSync(srcpath)
|
|
.filter(file => file !== 'resumes.js' && file !== 'template.vue' && file !== 'options.js');
|
|
};
|
|
|
|
convert();
|