|
@@ -2,52 +2,129 @@ const logger = require('@overleaf/logger')
|
|
|
const pug = require('pug')
|
|
const pug = require('pug')
|
|
|
const globby = require('globby')
|
|
const globby = require('globby')
|
|
|
const Settings = require('@overleaf/settings')
|
|
const Settings = require('@overleaf/settings')
|
|
|
-const path = require('path')
|
|
|
|
|
|
|
+const fs = require('fs')
|
|
|
|
|
+const Path = require('path')
|
|
|
|
|
|
|
|
// Generate list of view names from app/views
|
|
// Generate list of view names from app/views
|
|
|
-
|
|
|
|
|
-const viewList = globby
|
|
|
|
|
- .sync('app/views/**/*.pug', {
|
|
|
|
|
- onlyFiles: true,
|
|
|
|
|
- concurrency: 1,
|
|
|
|
|
- ignore: '**/_*.pug',
|
|
|
|
|
- })
|
|
|
|
|
- .concat(
|
|
|
|
|
- globby.sync('modules/*/app/views/**/*.pug', {
|
|
|
|
|
|
|
+function buildViewList() {
|
|
|
|
|
+ return globby
|
|
|
|
|
+ .sync('app/views/**/*.pug', {
|
|
|
onlyFiles: true,
|
|
onlyFiles: true,
|
|
|
concurrency: 1,
|
|
concurrency: 1,
|
|
|
- ignore: '**/_*.pug',
|
|
|
|
|
|
|
+ ignore: [
|
|
|
|
|
+ // Ignore includes
|
|
|
|
|
+ '**/_*.pug',
|
|
|
|
|
+ '**/_*/**',
|
|
|
|
|
+ // Ignore shared layout files
|
|
|
|
|
+ 'app/views/layout*',
|
|
|
|
|
+ 'app/views/layout/*',
|
|
|
|
|
+ ],
|
|
|
})
|
|
})
|
|
|
|
|
+ .concat(
|
|
|
|
|
+ globby.sync('modules/*/app/views/**/*.pug', {
|
|
|
|
|
+ onlyFiles: true,
|
|
|
|
|
+ concurrency: 1,
|
|
|
|
|
+ // Ignore includes
|
|
|
|
|
+ ignore: ['**/_*.pug', '**/_*/**'],
|
|
|
|
|
+ })
|
|
|
|
|
+ )
|
|
|
|
|
+ .concat(Object.values(Settings.viewIncludes).flat())
|
|
|
|
|
+ .map(x => Path.resolve(x))
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const PUG_COMPILE_ARGUMENTS = {
|
|
|
|
|
+ doctype: 'html',
|
|
|
|
|
+ cache: true,
|
|
|
|
|
+ compileDebug: Settings.debugPugTemplates,
|
|
|
|
|
+ inlineRuntimeFunctions: false,
|
|
|
|
|
+ module: true,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function precompileViewsAndCacheToDisk() {
|
|
|
|
|
+ const startTime = Date.now()
|
|
|
|
|
+ let success = 0
|
|
|
|
|
+ let precompiled = 0
|
|
|
|
|
+ for (const filename of buildViewList()) {
|
|
|
|
|
+ const precompiledFilename = filename.replace(/\.pug$/, '.js')
|
|
|
|
|
+ try {
|
|
|
|
|
+ const src = pug.compileFileClient(filename, PUG_COMPILE_ARGUMENTS)
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (fs.readFileSync(precompiledFilename, 'utf-8') === src) {
|
|
|
|
|
+ precompiled++
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch {}
|
|
|
|
|
+ fs.writeFileSync(precompiledFilename, src, {
|
|
|
|
|
+ encoding: 'utf-8',
|
|
|
|
|
+ mode: 0o644,
|
|
|
|
|
+ })
|
|
|
|
|
+ success++
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ logger.err({ err, filename }, 'failed to precompile pug template')
|
|
|
|
|
+ throw err
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ logger.info(
|
|
|
|
|
+ { timeTaken: Date.now() - startTime, success, precompiled },
|
|
|
|
|
+ 'compiled pug templates'
|
|
|
)
|
|
)
|
|
|
- .map(x => {
|
|
|
|
|
- return x.replace(/\.pug$/, '') // strip trailing .pug extension
|
|
|
|
|
- })
|
|
|
|
|
- .filter(x => {
|
|
|
|
|
- return !/^_/.test(x)
|
|
|
|
|
- })
|
|
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
module.exports = {
|
|
module.exports = {
|
|
|
|
|
+ compileViewIncludes(app) {
|
|
|
|
|
+ const viewIncludes = {}
|
|
|
|
|
+ for (const [view, paths] of Object.entries(Settings.viewIncludes)) {
|
|
|
|
|
+ viewIncludes[view] = []
|
|
|
|
|
+ for (const filePath of paths) {
|
|
|
|
|
+ viewIncludes[view].push(
|
|
|
|
|
+ pug.compileFile(filePath, {
|
|
|
|
|
+ ...PUG_COMPILE_ARGUMENTS,
|
|
|
|
|
+ cache: app.enabled('view cache'),
|
|
|
|
|
+ })
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return viewIncludes
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
precompileViews(app) {
|
|
precompileViews(app) {
|
|
|
const startTime = Date.now()
|
|
const startTime = Date.now()
|
|
|
let success = 0
|
|
let success = 0
|
|
|
|
|
+ let precompiled = 0
|
|
|
let failures = 0
|
|
let failures = 0
|
|
|
- viewList.forEach(view => {
|
|
|
|
|
- const filename = path.resolve(view + '.pug') // express views are cached using the absolute path
|
|
|
|
|
|
|
+ for (const filename of buildViewList()) {
|
|
|
|
|
+ const precompiledFilename = filename.replace(/\.pug$/, '.js')
|
|
|
|
|
+ if (fs.existsSync(precompiledFilename)) {
|
|
|
|
|
+ logger.debug({ filename }, 'loading precompiled pug template')
|
|
|
|
|
+ try {
|
|
|
|
|
+ pug.cache[filename] = require(precompiledFilename)
|
|
|
|
|
+ precompiled++
|
|
|
|
|
+ continue
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ logger.error(
|
|
|
|
|
+ { filename, err },
|
|
|
|
|
+ 'error loading precompiled pug template'
|
|
|
|
|
+ )
|
|
|
|
|
+ failures++
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
try {
|
|
try {
|
|
|
- pug.compileFile(filename, {
|
|
|
|
|
- cache: true,
|
|
|
|
|
- compileDebug: Settings.debugPugTemplates,
|
|
|
|
|
- })
|
|
|
|
|
- logger.debug({ filename }, 'compiled')
|
|
|
|
|
|
|
+ logger.warn({ filename }, 'compiling pug template at boot time')
|
|
|
|
|
+ pug.compileFile(filename, PUG_COMPILE_ARGUMENTS)
|
|
|
success++
|
|
success++
|
|
|
} catch (err) {
|
|
} catch (err) {
|
|
|
- logger.error({ filename, err: err.message }, 'error compiling')
|
|
|
|
|
|
|
+ logger.error({ filename, err }, 'error compiling pug template')
|
|
|
failures++
|
|
failures++
|
|
|
}
|
|
}
|
|
|
- })
|
|
|
|
|
|
|
+ }
|
|
|
logger.debug(
|
|
logger.debug(
|
|
|
- { timeTaken: Date.now() - startTime, failures, success },
|
|
|
|
|
- 'compiled templates'
|
|
|
|
|
|
|
+ { timeTaken: Date.now() - startTime, failures, success, precompiled },
|
|
|
|
|
+ 'compiled pug templates'
|
|
|
)
|
|
)
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+if (require.main === module) {
|
|
|
|
|
+ precompileViewsAndCacheToDisk()
|
|
|
|
|
+ process.exit(0)
|
|
|
|
|
+}
|