-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.js
executable file
·237 lines (189 loc) · 7.06 KB
/
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
#!/usr/bin/env node
const puppeteer = require('puppeteer');
const _ = require('lodash');
const { writeFile, updateManifest } = require('./lib/output');
const {
options,
definitions,
baseUrl,
manifest,
log,
logVerbose,
} = require('./lib/cli-options');
const { beautify } = require('./lib/beautify');
const {
prepareCookies, processIncludes, processBricks, processSnippets
} = require('./lib/browser');
const {
twigBasepath,
getTwigPath,
} = require('./lib/paths-names');
const {
convertToTwig, fixCssPaths, fixJsPaths, disableNavigation
} = require('./lib/page-actions');
const { run: thumbnails } = require('./lib/thumbnails');
/*
Since we will be doing a lot of async processing,
we need to catch unhandled rejections.
*/
process.on('unhandledRejection', (error) => {
log('error', 'Uh-oh unhandled promise rejection');
// eslint-disable-next-line no-console
console.error(error);
log('fatal', 'Exiting now');
process.exit(1);
});
const globalCookies = definitions.cookies || [];
/**
* Defines which URLs (`path`) should be parsed and which Twig template
* (`template`) it will be written to.
* @type {{template: string, path: string}[]}
*/
const { pages } = definitions;
log('start', 'Starting with pre-rendering');
/**
* Parse a page and generate corresponding files.
* @param {{template: String, path: String}} pageDefinition Config for this page
* @returns {Promise<void>}
*/
async function parsePageDefinition(pageDefinition) {
/*
IMPORTANT: everything inside of page.evaluate(() => { ... }) is executed
**inside** the Puppeteer instance i.e. a Chromium (or Firefox) browser.
*/
logVerbose('await', 'Starting browser');
const browserOptions = _.defaultsDeep(
{},
pageDefinition.browser || {},
{
defaultViewport: {
width: 1920, height: 1080, deviceScaleFactor: 1, isMobile: false, hasTouch: false
}
},
definitions.browser || {},
options.debug ? {
headless: false,
devtools: true,
slowMo: 250,
} : {}
);
if (options.firefox) {
browserOptions.product = 'firefox';
log('warn', 'If this fails, please run: PUPPETEER_PRODUCT=firefox npm i puppeteer');
}
const browser = await puppeteer.launch(browserOptions);
const page = await browser.newPage();
const {
cookies = [],
path,
} = pageDefinition;
await page.setCookie(...prepareCookies(...globalCookies, ...cookies));
if (options.debug) {
page.on('console', msg => log('debug', '%s: console.%s(): %s', path, msg.type(), msg.text()));
}
await (pageDefinition.start || definitions.start || function() {})({
pageDefinition, browser, page, log
});
log('start', path);
logVerbose('await', `Loading page ${path}`);
// attempt to append query string to the URL, handles the case with an existing query string and no query string
const pageUrl = `${baseUrl}${path}${path.includes('?') ? '&' : '?'}is_generator=1`;
await page.goto(pageUrl, { waitUntil: 'networkidle0' });
await (pageDefinition.preParse || definitions.preParse || function() {})({
pageDefinition, browser, page, log
});
// Replace #app id to prevent Vue initialization in Pimcore live edit
/**
* TODO: this seems to cause a timeout and a Vue exception.
* Still, it seems to be required, if removed, invalid templates are generated (content is not reduced).
* Best would be to create a clone of the DOM and work with the clone instead of manipulating the DOM.
* @see https://pptr.dev/#?product=Puppeteer&version=v2.1.1&show=api-pageevaluatepagefunction-args
* const bodyHandle = await page.$('body');
* const html = await page.evaluate(body => body.innerHTML, bodyHandle);
* await bodyHandle.dispose();
*/
await page.evaluate(() => {
const app = document.getElementById('app');
if (!app) {
return;
}
app.id = 'not-app-anymore';
});
logVerbose('note', 'Disabling navigation for admin mode');
await disableNavigation(page);
logVerbose('note', 'Converting page to Twig - Twig includes');
await processIncludes(page);
/* Extract Twig for Snippets */
logVerbose('note', 'Extracting snippets');
await processSnippets(page);
/* Extract Twig for Areabricks */
logVerbose('note', 'Processing areabricks');
await processBricks(page);
/* Extract Twig for document itself */
logVerbose('note', 'Converting page to Twig - Pimcore tags');
await convertToTwig(page);
logVerbose('note', 'Fixing JS paths');
await fixJsPaths(page);
logVerbose('note', 'Fixing CSS paths');
await fixCssPaths(page);
await (pageDefinition.postParse || definitions.postParse || function() {})({
pageDefinition, browser, page, log
});
// Create page template, if defined in generator-definitions
if (pageDefinition.templatePath) {
logVerbose('note', `Writing Twig template as ${pageDefinition.template}`);
const templateName = await page.evaluate(() => {
const template = document.querySelector('[data-pimcore-template]');
return (template && template.dataset.pimcoreTemplate) || null;
});
if (!templateName) {
log('error', 'No template name was defined for the template in "%s".', path);
throw new Error(`No template name was defined for the template in '${path}'.`);
}
const templateStump = `${pageDefinition.templatePath}/${templateName}`;
const baseTemplate = pageDefinition.baseTemplate || definitions.twig.baseTemplate;
const extendsTemplate = baseTemplate
? `{% extends '${baseTemplate}' %}\n`
: '';
await writeFile(getTwigPath(templateStump),
`${extendsTemplate}{% block app %}
{% if editmode %}
{% include '${getTwigPath(`${templateStump}_edit`).replace(twigBasepath, '').substr(1)}' %}
{% else %}
{% include '${getTwigPath(`${templateStump}_view`).replace(twigBasepath, '').substr(1)}' %}
{% endif %}
{% endblock %}
`);
// Extract body from puppeteer
let bodyHTML = await page.evaluate(() => {
const template = document.querySelector('[data-pimcore-template]') || document.documentElement;
delete template.dataset.pimcoreTemplate;
return template.outerHTML; // TODO: remove any other extractions (e.g. data-pimcore-areabrick elements)
});
await writeFile(getTwigPath(`${templateStump}_view`), beautify(bodyHTML));
bodyHTML = bodyHTML.replace(/disabled-in-editmode/g, "{{ editmode ? 'disabled' : '' }}");
await writeFile(getTwigPath(`${templateStump}_edit`), beautify(bodyHTML));
}
await (pageDefinition.done || definitions.done || function() {})({
pageDefinition, browser, page, log
});
log('complete', 'Done with page %s.', path);
if (!options.debug) {
await browser.close();
}
}
const promises = [];
// eslint-disable-next-line no-restricted-syntax
for (const page of pages) {
promises.push(parsePageDefinition(page));
}
thumbnails();
Promise.all(promises)
.then(() => {
updateManifest(manifest);
log('complete', 'Finished pre-rendering');
if (options.debug) {
log('info', 'Keeping generator running to allow for debugging.');
setInterval(() => log('note', 'Still running. Use Ctrl+C to stop'), 10000);
}
});