Refactor afterPack hook to be more maintainable
Nothing is hardcoded in the hook any more, everything can be configured through the electron-builder configuration.
This commit is contained in:
parent
ed071d0996
commit
67905e01a7
148
afterpack.js
148
afterpack.js
|
@ -1,14 +1,25 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
/*
|
/*
|
||||||
This is a hack to get around the issues with electron-builder not including nested node_modules
|
This is a hack to get around the issues with electron-builder not including nested node_modules.
|
||||||
We do this by simply making the asar ourselves using the parameters from the build.
|
We do this by simply making the asar ourselves using the parameters from the build.
|
||||||
|
This takes the asar file that we have created, unpacks it, then copies in the nested node_modules
|
||||||
|
directories from the app directory.
|
||||||
|
|
||||||
|
This also preserves any asar unpacked files that you may have had in the build, and unpacks any
|
||||||
|
nested node_modules files that might have been unpacked as well.
|
||||||
|
|
||||||
|
This also assumes that you have some sort of beforeBuild hook that already trims and massages the
|
||||||
|
nested node_modules files to be what you want; if not, you may have some bloat (and some potential
|
||||||
|
breakage)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Make sure you have these in your devDependencies in your root project.
|
||||||
const glob = require('fast-glob');
|
const glob = require('fast-glob');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const { promisify } = require('util')
|
const { promisify } = require('util')
|
||||||
const rimraf = promisify(require('rimraf'))
|
const rimraf = promisify(require('rimraf'))
|
||||||
const asar = require('asar');
|
const asar = require('asar');
|
||||||
|
const micromatch = require("micromatch")
|
||||||
|
|
||||||
const commonExclude = [
|
const commonExclude = [
|
||||||
"!**/{test,__tests__,tests,powered-test,example,examples,CHANGELOG.md,README.md,README,readme.md,readme}",
|
"!**/{test,__tests__,tests,powered-test,example,examples,CHANGELOG.md,README.md,README,readme.md,readme}",
|
||||||
|
@ -21,79 +32,110 @@ const commonExclude = [
|
||||||
"!**/{npm-debug.log,yarn.lock,.yarn-integrity,.yarn-metadata.json}"
|
"!**/{npm-debug.log,yarn.lock,.yarn-integrity,.yarn-metadata.json}"
|
||||||
]
|
]
|
||||||
|
|
||||||
exports.default = async function afterPackHook(context){
|
function concatUnique(a,b) {
|
||||||
|
if (!Array.isArray(a) || !Array.isArray(b)){
|
||||||
|
throw Error("both parameters must be arrays")
|
||||||
|
}
|
||||||
|
let arr = a.concat(b)
|
||||||
|
let seen = {};
|
||||||
|
return arr.filter(function(item) {
|
||||||
|
return seen.hasOwnProperty(item) ? false : (seen[item] = true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// we're using the platform-specific parameters over the global parameters.
|
||||||
|
function globalOrPlatformParam(local,global){
|
||||||
|
if (typeof local != 'undefined' && local.length != 0){
|
||||||
|
return local;
|
||||||
|
} else if (typeof global != 'undefined' && global.length != 0){
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function globalOrPlatformArrayParam(local, global){
|
||||||
|
let param = globalOrPlatformParam(local, global) || []
|
||||||
|
if (typeof param == "string"){
|
||||||
|
param = [param]
|
||||||
|
}
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.default = async function afterPackHook(context){
|
||||||
|
// Get build parameters from the context.
|
||||||
const appDir = context.packager.info._appDir + "/"
|
const appDir = context.packager.info._appDir + "/"
|
||||||
const platform = context.packager.platform.nodeName
|
const platform = context.packager.platform.nodeName
|
||||||
|
const globalFilesParam = context.packager.info._configuration.files;
|
||||||
|
const platformFilesParam = context.packager.platformSpecificBuildOptions.files
|
||||||
|
const globalUnpackAsarParam = context.packager.info._configuration.asarUnpack;
|
||||||
|
const platformUnpackAsarParam = context.packager.platformSpecificBuildOptions.asarUnpack;
|
||||||
|
|
||||||
let resourcesDir = context.appOutDir + "/resources/"
|
let resourcesDir = context.appOutDir + "/resources/"
|
||||||
// exception for resources dir for mac
|
// exception for resources dir for mac; win and linux use the same directory.
|
||||||
if (platform == "darwin"){
|
if (platform == "darwin"){
|
||||||
resourcesDir = context.appOutDir + "/" + context.packager.appInfo.productFilename + ".app/Contents/Resources/"
|
resourcesDir = context.appOutDir + "/" + context.packager.appInfo.productFilename + ".app/Contents/Resources/"
|
||||||
}
|
}
|
||||||
|
|
||||||
const asarAppDir = resourcesDir + "app/"
|
const asarAppUnpackedDir = resourcesDir + "app.asar.unpacked/"
|
||||||
|
|
||||||
let globPatterns = context.packager.platformSpecificBuildOptions.files || context.packager._configuration.files || []
|
// The Platform-specific build options override the common config, so attempt to use those first.
|
||||||
let asarUnpackPattern = context.packager.platformSpecificBuildOptions.asarUnpack
|
let globPatterns = globalOrPlatformArrayParam(platformFilesParam, globalFilesParam)
|
||||||
|
|
||||||
// a limitation of this method is that the asarUnpack option can only be a single string
|
|
||||||
if (typeof asarUnpackPattern != "string"){
|
|
||||||
if (Array.isArray(asarUnpackPattern) && asarUnpackPattern.length == 1){
|
|
||||||
asarUnpackPattern = asarUnpackPattern[0]
|
|
||||||
} else {
|
|
||||||
throw Error("asarUnpack pattern can only be one string!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove current asar files
|
|
||||||
await rimraf(resourcesDir + "app.asar")
|
|
||||||
await rimraf(resourcesDir + "app")
|
|
||||||
await rimraf(resourcesDir + "app.asar.unpacked")
|
|
||||||
|
|
||||||
// electron-builder automatically adds this to the files parameter, so we have to too
|
// electron-builder automatically adds this to the files parameter, so we have to too
|
||||||
if (!globPatterns.includes("**/*")){
|
if (!globPatterns.includes("**/*")){
|
||||||
globPatterns.push("**/*")
|
globPatterns.push("**/*")
|
||||||
}
|
}
|
||||||
|
|
||||||
globPatterns = globPatterns.concat(commonExclude)
|
// Add the common exclusions to the patterns
|
||||||
|
globPatterns = concatUnique(globPatterns,commonExclude);
|
||||||
|
|
||||||
// Take the files from the app directory and copy them to make the asar.app directory
|
// Find all the nested node_modules files in the app directory
|
||||||
// according to the glob specified in the electron-builder config for this target
|
let nestedNMFiles = glob.sync("+(**/node_modules/**/*|!node_modules/**/*)", {cwd: appDir, dot:true})
|
||||||
let files = glob.sync(globPatterns, { dot:true, cwd: appDir})
|
|
||||||
|
|
||||||
await new Promise ((resolve) => {
|
// filter out the ones not matched by the file globs in the builder config
|
||||||
files.forEach(async (file, index, array)=>{
|
let filteredNestedFiles = micromatch(nestedNMFiles, globPatterns, {matchBase:true})
|
||||||
await fs.copy(appDir + file, asarAppDir + file)
|
|
||||||
if (index == array.length -1){
|
// This is the directory we're copying everything to, to create the asar
|
||||||
resolve()
|
let asarAppDir = resourcesDir + "app/"
|
||||||
}
|
|
||||||
|
// Get a listing of all the files in the app.asar.unpacked dir, so we can create a franken-glob
|
||||||
|
// to pass to asar when packing.
|
||||||
|
let unpackedFileList = glob.sync("**/*", {cwd:asarAppUnpackedDir, dot:true})
|
||||||
|
let unpackPattern = "{"
|
||||||
|
unpackedFileList.forEach((file,idx,arr)=>{
|
||||||
|
unpackPattern += asarAppDir + file + ','
|
||||||
})
|
})
|
||||||
}).catch(console.error)
|
|
||||||
|
|
||||||
// The only part that's hardcoded and not dependent on the electron-build config,
|
// Get any user-defined asarUnpack patterns in case we need to unpack some hoisted node_modules,
|
||||||
// remove the unnecessary node-native files from the asar.app dir
|
// and add any to the pattern.
|
||||||
let unpackedFiles = glob.sync(asarUnpackPattern, {dot:true, cwd: asarAppDir})
|
let userUnpackGlob = globalOrPlatformArrayParam(platformUnpackAsarParam, globalUnpackAsarParam)
|
||||||
await new Promise ((resolve) => {
|
let nestedFilesToUnpack = micromatch(nestedNMFiles, userUnpackGlob, {matchBase:true})
|
||||||
unpackedFiles.forEach(async (file, index, array)=>{
|
nestedFilesToUnpack.forEach((file)=>{
|
||||||
if (platform == "win32") {
|
unpackPattern += asarAppDir + file + ','
|
||||||
if (file.includes(".node") && (file.includes("_linux") || file.includes("_darwin"))){
|
|
||||||
await fs.remove(asarAppDir + file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
if (file.includes(".node") && !file.includes("_" + platform) ){
|
|
||||||
await fs.remove(asarAppDir + file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index == array.length -1){
|
|
||||||
resolve()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}).catch(console.error)
|
// trailing commas don't matter in this pattern.
|
||||||
|
unpackPattern += '}'
|
||||||
|
|
||||||
// build the asar from the newly created app dir, unpacking the files necessary according to the glob
|
// Combine the nested files to unpack with the filtered nested files, as electron-builder
|
||||||
await asar.createPackageWithOptions(asarAppDir, resourcesDir + "app.asar", {unpack: asarUnpackPattern})
|
// sometimes does weird things when file globs and the asarUnpack globs are used together;
|
||||||
|
// Files that don't match the file globs but do match the asarUnpack glob are included in the build.
|
||||||
|
let nestedFilesToCopy = concatUnique(filteredNestedFiles, nestedFilesToUnpack);
|
||||||
|
|
||||||
|
// Now we have the necessary file lists, create the asar.
|
||||||
|
|
||||||
|
// Unpack everything in app.asar to the resources/app dir.
|
||||||
|
// This also copies all the files in 'app.asar.unpacked' to here as well.
|
||||||
|
asar.extractAll(resourcesDir + "app.asar", asarAppDir)
|
||||||
|
await rimraf(resourcesDir + "app.asar")
|
||||||
|
await rimraf(asarAppUnpackedDir)
|
||||||
|
|
||||||
|
// Copy the nested node_modules files to the extracted directory.
|
||||||
|
nestedFilesToCopy.forEach((file)=>{
|
||||||
|
fs.copySync(appDir + file, asarAppDir + file)
|
||||||
|
})
|
||||||
|
// build the asar
|
||||||
|
await asar.createPackageWithOptions(asarAppDir, resourcesDir + "app.asar", {unpack: unpackPattern})
|
||||||
await rimraf(asarAppDir)
|
await rimraf(asarAppDir)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,36 +4,58 @@ productName: Lightcord
|
||||||
directories:
|
directories:
|
||||||
app: distApp
|
app: distApp
|
||||||
output: builds
|
output: builds
|
||||||
|
# This is to get around the bug in electron-builder with not including nested node_modules.
|
||||||
afterPack: "./afterpack.js"
|
afterPack: "./afterpack.js"
|
||||||
win:
|
win:
|
||||||
target:
|
target:
|
||||||
- target: zip
|
- target: zip
|
||||||
arch:
|
arch:
|
||||||
- ia32
|
- ia32
|
||||||
|
- target: portable
|
||||||
|
arch:
|
||||||
|
- ia32
|
||||||
icon: app.ico
|
icon: app.ico
|
||||||
publisherName: Lightcord
|
publisherName: Lightcord
|
||||||
files:
|
files:
|
||||||
|
- "**/*"
|
||||||
- "!**/*.ts"
|
- "!**/*.ts"
|
||||||
- "!**/*.so"
|
- "!**/*.so"
|
||||||
- "!**/*.4"
|
- "!**/*.4"
|
||||||
- "!**/*.dylib"
|
- "!**/*.dylib"
|
||||||
asarUnpack: "**/*.{node,dll}"
|
- "!**/*_darwin.node"
|
||||||
|
- "!**/*_linux.node"
|
||||||
|
# This gets passed to the afterPack hook to help recreate the asar file. This must be a single string.
|
||||||
|
asarUnpack:
|
||||||
|
- "**/*.node"
|
||||||
|
- "**/*.dll"
|
||||||
linux:
|
linux:
|
||||||
target:
|
target:
|
||||||
- zip
|
- zip
|
||||||
|
- AppImage
|
||||||
icon: discord.png
|
icon: discord.png
|
||||||
files:
|
files:
|
||||||
|
- "**/*"
|
||||||
- "!**/*.ts"
|
- "!**/*.ts"
|
||||||
- "!**/*.dll"
|
- "!**/*.dll"
|
||||||
- "!**/*.dylib"
|
- "!**/*.dylib"
|
||||||
asarUnpack: "**/*.{node,so.4}"
|
- "!**/discord*.node"
|
||||||
|
- "**/*_linux.node"
|
||||||
|
asarUnpack:
|
||||||
|
- "**/*_linux.node"
|
||||||
|
- "**/*.so.4"
|
||||||
mac:
|
mac:
|
||||||
target:
|
target:
|
||||||
- zip
|
- zip
|
||||||
icon: app_icon_darwin.icns
|
icon: app_icon_darwin.icns
|
||||||
files:
|
files:
|
||||||
|
- "**/*"
|
||||||
- "!**/*.ts"
|
- "!**/*.ts"
|
||||||
- "!**/*.dll"
|
- "!**/*.dll"
|
||||||
- "!**/*.so"
|
- "!**/*.so"
|
||||||
- "!**/*.4"
|
- "!**/*.4"
|
||||||
asarUnpack: "**/*.{node,dylib}"
|
- "!**/discord*.node"
|
||||||
|
- "**/*_darwin.node"
|
||||||
|
asarUnpack:
|
||||||
|
- "**/*_linux.node"
|
||||||
|
- "**/*.dylib"
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,12 @@
|
||||||
"integrity": "sha512-+KQ+/koZ7sJXnf5cnCANofY6yXAdYJNEoVZEuWcwJfuWbUp9u6l09I7KhwD+ivU+cdz7JId4V5ukxscWtHdSuw==",
|
"integrity": "sha512-+KQ+/koZ7sJXnf5cnCANofY6yXAdYJNEoVZEuWcwJfuWbUp9u6l09I7KhwD+ivU+cdz7JId4V5ukxscWtHdSuw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/braces": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/braces/-/braces-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-TbH79tcyi9FHwbyboOKeRachRq63mSuWYXOflsNO9ZyE5ClQ/JaozNKl+aWUq87qPNsXasXxi2AbgfwIJ+8GQw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/color-name": {
|
"@types/color-name": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
||||||
|
@ -141,6 +147,15 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/micromatch": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/micromatch/-/micromatch-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-my6fLBvpY70KattTNzYOK6KU1oR1+UCz9ug/JbcF5UrEmeCt9P7DV2t7L8+t18mMPINqGQCE4O8PLOPbI84gxw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/braces": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/minimatch": {
|
"@types/minimatch": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/auto-launch": "^5.0.1",
|
"@types/auto-launch": "^5.0.1",
|
||||||
"@types/electron-devtools-installer": "^2.2.0",
|
"@types/electron-devtools-installer": "^2.2.0",
|
||||||
|
"@types/micromatch": "^4.0.1",
|
||||||
"@types/mkdirp": "^1.0.0",
|
"@types/mkdirp": "^1.0.0",
|
||||||
"@types/node": "12.12.39",
|
"@types/node": "12.12.39",
|
||||||
"@types/rimraf": "^3.0.0",
|
"@types/rimraf": "^3.0.0",
|
||||||
|
@ -48,6 +49,7 @@
|
||||||
"electron-builder": "^22.8.0",
|
"electron-builder": "^22.8.0",
|
||||||
"fast-glob": "^3.2.4",
|
"fast-glob": "^3.2.4",
|
||||||
"fs-extra": "^9.0.1",
|
"fs-extra": "^9.0.1",
|
||||||
|
"micromatch": "^4.0.2",
|
||||||
"terser": "^4.7.0",
|
"terser": "^4.7.0",
|
||||||
"typescript": "^3.9.7",
|
"typescript": "^3.9.7",
|
||||||
"yazl": "^2.5.1"
|
"yazl": "^2.5.1"
|
||||||
|
|
Loading…
Reference in New Issue