2020-11-07 17:59:37 +02:00
const utils = require ( '@joplin/tools/gulp/utils' ) ;
2024-08-17 13:22:35 +02:00
const { toForwardSlashes } = require ( '@joplin/utils/path' ) ;
2019-12-29 19:58:40 +02:00
const fs = require ( 'fs-extra' ) ;
2024-08-02 15:51:49 +02:00
const path = require ( 'path' ) ;
2019-12-29 19:58:40 +02:00
const md5 = require ( 'md5' ) ;
2020-02-21 00:59:18 +02:00
const rootDir = ` ${ _ _dirname } /.. ` ;
2019-12-29 19:58:40 +02:00
const outputDir = ` ${ rootDir } /pluginAssets ` ;
2020-03-14 01:46:14 +02:00
const walk = function ( dir ) {
let results = [ ] ;
const list = fs . readdirSync ( dir ) ;
2023-06-30 10:39:21 +02:00
// eslint-disable-next-line github/array-foreach -- Old code before rule was applied
2023-02-20 17:02:29 +02:00
list . forEach ( ( file ) => {
2019-12-29 19:58:40 +02:00
file = ` ${ dir } / ${ file } ` ;
2020-03-14 01:46:14 +02:00
const stat = fs . statSync ( file ) ;
2019-12-29 19:58:40 +02:00
if ( stat && stat . isDirectory ( ) ) {
results = results . concat ( walk ( file ) ) ;
} else {
results . push ( file ) ;
}
} ) ;
return results ;
} ;
2024-08-17 13:22:35 +02:00
const readAsBase64 = async ( path , mime ) => {
let buffer ;
// Normalize line endings to prevent hashes from changing when recompiling on
// Windows (if originally compiled on Unix).
if ( mime === 'application/javascript' || mime . startsWith ( 'text/' ) ) {
const file = await fs . readFile ( path , 'utf-8' ) ;
buffer = Buffer . from ( file . replace ( /\r\n/g , '\n' ) , 'utf-8' ) ;
} else {
buffer = await fs . readFile ( path ) ;
}
return buffer . toString ( 'base64' ) ;
} ;
2023-07-18 20:31:08 +02:00
2024-08-17 13:22:35 +02:00
async function encodeFile ( sourcePath , destPath ) {
2023-07-18 20:31:08 +02:00
const ext = utils . fileExtension ( sourcePath ) . toLowerCase ( ) ;
let mime = 'application/octet-stream' ;
if ( ext === 'js' ) mime = 'application/javascript' ;
if ( ext === 'css' ) mime = 'text/css' ;
2024-08-17 13:22:35 +02:00
const base64Data = await readAsBase64 ( sourcePath , mime ) ;
const hash = md5 ( base64Data ) ;
const js = ` module.exports = \` ${ base64Data } \` ; ` ;
const outputPath = ` ${ outputDir } / ${ destPath } .base64.js ` ;
console . info ( ` Encoding " ${ sourcePath } " => " ${ outputPath } " ` ) ;
await utils . mkdirp ( utils . dirname ( outputPath ) ) ;
await fs . writeFile ( outputPath , js ) ;
2023-07-18 20:31:08 +02:00
return {
encoding : 'base64' ,
name : destPath ,
encodedName : ` ${ destPath } .base64.js ` ,
mime : mime ,
hash : hash ,
} ;
}
async function main ( ) {
2022-10-14 12:05:21 +02:00
for ( let i = 0 ; i < 3 ; i ++ ) {
try {
2023-07-18 20:31:08 +02:00
await fs . remove ( outputDir ) ;
await utils . mkdirp ( outputDir ) ;
const encodedFiles = [ ] ;
const sourceAssetDir = ` ${ rootDir } /../renderer/assets ` ;
const files = walk ( sourceAssetDir ) ;
2019-12-29 19:58:40 +02:00
2023-07-18 20:31:08 +02:00
for ( const file of files ) {
const destFile = file . substr ( sourceAssetDir . length + 1 ) ;
encodedFiles . push ( await encodeFile ( file , destFile ) ) ;
}
2019-12-29 19:58:40 +02:00
2023-07-18 20:31:08 +02:00
const hashes = [ ] ;
const indexJs = [ ] ;
for ( const file of encodedFiles ) {
indexJs . push ( ` ' ${ file . name } ': { data: require('./ ${ file . encodedName } '), mime: ' ${ file . mime } ', encoding: ' ${ file . encoding } ' }, ` ) ;
hashes . push ( file . hash ) ;
}
const hash = md5 ( hashes . join ( '' ) ) ;
await fs . writeFile ( ` ${ outputDir } /index.js ` , ` module.exports = { \n hash:" ${ hash } ", files: { \n ${ indexJs . join ( '\n' ) } \n } \n }; ` ) ;
2024-08-02 15:51:49 +02:00
await fs . writeFile ( ` ${ outputDir } /index.web.js ` , ` module.exports = ${ JSON . stringify ( {
hash ,
2024-08-17 13:22:35 +02:00
files : files . map ( file => toForwardSlashes ( path . relative ( sourceAssetDir , file ) ) ) ,
2024-08-02 15:51:49 +02:00
} ) } ` );
2023-07-18 20:31:08 +02:00
return ;
2022-10-14 12:05:21 +02:00
} catch ( error ) {
2023-07-18 20:31:08 +02:00
// Although it makes no sense, the above function `encodeFile()` sometimes fails on CI
2022-10-14 12:05:21 +02:00
// with error "DEST does not exist", which of course it doesn't
// since we are trying to create it. So here we retry when it happens.
//
// Full error:
//
// Encoding "/home/runner/work/joplin/joplin/packages/app-mobile/tools/../../renderer/assets/katex/fonts/KaTeX_Math-BoldItalic.woff2" => "/home/runner/work/joplin/joplin/packages/app-mobile/tools/../pluginAssets/katex/fonts/KaTeX_Math-BoldItalic.woff2.base64.js"
// 'encodeAssets' errored after 115 ms
// Error: ENOENT: no such file or directory, open '/home/runner/work/joplin/joplin/packages/app-mobile/tools/../pluginAssets/katex/fonts/KaTeX_Math-BoldItalic.woff2.base64.js'
2023-07-18 20:31:08 +02:00
//
// On CI we also get the below random error, which also doesn't make sense since `remove()`
// should delete the directory whether it's empty or not.
//
// Error: ENOTEMPTY: directory not empty, rmdir '/Users/runner/work/joplin/joplin/packages/app-mobile/tools/../pluginAssets'
2022-10-14 12:05:21 +02:00
2023-07-18 20:31:08 +02:00
console . warn ( ` Could not encode assets ( ${ i } ). Will try again... ` ) ;
2022-10-14 12:05:21 +02:00
console . warn ( 'Error was:' , error ) ;
await utils . msleep ( 1000 + 1000 * i ) ;
continue ;
}
}
throw new Error ( 'Could not encode file after multiple attempts. See above for errors.' ) ;
2019-12-29 19:58:40 +02:00
}
2020-02-21 00:59:18 +02:00
module . exports = main ;