当前位置: 首页 > 后端技术 > Node.js

使用pkg打包ThinkJS项目

时间:2023-04-03 17:20:32 Node.js

在ThinkJS用户群中,经常有开发者要求对源码进行加密保护。我们知道JavaScript是一门动态语言,不像其他静态语言可以编译成二进制包来防止源代码泄露。所以才出现了pkg、nexe等工具,支持将JS代码和Node一起打包成一个可执行文件。一是解决了环境依赖的问题,二是解决了大家关心的源码保护问题。在pkg模块的README中,列出了它的几个用途。如果你有以下需求,不妨试一试。为应用提供商业发布,不暴露源码为应用提供demo,不暴露源码一键打包所有平台可执行文件,无需相应平台环境,提供自解压或自安装解决方案无需安装Node.js即可运行应用使用npm部署只需要一个文件,无需通过npm安装大量依赖资源。打包后,使应用迁移更加方便。在不安装对应版本的情况下,在指定的Node.js版本下测试应用。pkg模块的使用方法基本使用可以看这篇文章《把你的NodeJS程序给没有NodeJS的人运行》。通过npminstall-gpkg全局安装模块后,可以在命令行使用pkg命令。除了在命令行指定参数外,pkg还支持在package.json中进行配置。{..."bin":"production.js","scripts":{"pkg":"pkg.--out-path=dist/"},"pkg":{"scripts":[...]"assets":[...],"targets":[...]},...}上面是一个简单的配置。bin用于指定最终打包的入口文件,pkg.scripts和pkg.assets用于指定除入口文件外还需要打包成可执行文件的内容,前者用于指定其他.js文件,后者用于指定Non-.js资源。pkg.targets用于指定需要打包的平台。平台名称结构如下,node${version}-${platform}-${arch}。version用于指定Node的具体版本,platform用于指定编译平台,可以是freebsd、linux、alpine、macos或win,最后arch用于指定编译平台的架构,可以是x64、x86、armv6或armv7。例如node10-macos-x64表示基于Node10在MacOS平台上打包执行的可执行程序。脚本、资产和目标都支持多个阵列配置。配置入口文件、依赖脚本和资源、编译平台后,执行npmrunpkg完成编译。ThinkJSpkg如何打包的原理大概是提供一个虚拟文件系统,修改__filename、__dirname等变量和官方API中的IO操作方法指向本地文件系统指向虚拟系统。通过虚拟文件系统读取压缩打包的程序源代码,为脚本执行提供环境。需要注意的是,虚拟文件??系统是只读的,所以如果程序中有根据__dirname进行读写的方法,需要避免。代码预处理会在ThinkJS工程中的以下两个地方有文件写入操作:工程启动后,会在runtime/config/${env}.json下写入最终的配置文件。在生产环境中,默认会在logs/目录下。这些目录默认是基于当前工程文件夹的,所以根据前面的理论需要避开。pkg的README告诉我们process.cwd()仍然会指向真实环境,所以我们可以修改上面目录的位置为process.cwd()来解决这个问题。//pkg.jsconstpath=require('path');constApplication=require('thinkjs');constinstance=newApplication({//可以在启动文件中自定义运行目录RUNTIME_PATH:path.join(process.cwd(),'runtime'),ROOT_PATH:__dirname,proxy:true,env:'pkg',});实例.run();我们在production.js的基础上,新建一个pkg.js启动文件,定义项目启动后的RUNTIME_PATH路径,并将env赋值给pkg,方便后续通过think.env==='pkg'切换配置配置。//src/config/adapter.jsconst{Console,DateFile}=require('think-logger3');constisDev=think.env==='development';constisPkg=think.env==='pkg';exports.logger={类型:isDev?'console':'dateFile',console:{handle:Console},dateFile:{handle:DateFile,级别:'ALL',绝对:true,模式:'-yyyy-MM-dd',alwaysIncludePattern:true,文件名:path.join(isPkg?process.cwd():think.ROOT_PATH,'logs/app.log')}};在adapter配置中,我们修改原来的路径是基于think.ROOT_PATH成基于process.cwd()。除了日志服务,如果业务中使用了缓存、session等服务,如果也是基于文件存储的,还需要修改相应的文件存储配置。当然,这些都是ThinkJS自带的一些服务。如果项目中使用了其他一些服务,或者业务逻辑涉及文件写入,则需要修改配置。避免了package配置项的写操作后,我们就可以正常配置pkg,然后进行打包了。一个简单的pkg模块配置如下所示://package.json{"bin":"pkg.js","pkg":{"assets":["src/**/*","view/**/*","www/**/*"],"targets":["node10-linux-x64","node10-macos-x64","node10-win-x64"]}}这里我们指定pkg.js作为打包的入口文件,指定linux、macos、win三个平台需要编译的可执行脚本,指定src/、view/、www/三个目录需要一起打包为资源.这是因为ThinkJS是一个动态require项目,具体的业务逻辑是通过在执行过程中遍历文件目录读取文件来加载的。对于pkg模块打包,这些依赖在编译时是无法获知的,所以需要将其打包在一起作为启动依赖的“资源”。配置完成后,直接在项目目录下执行pkg。如果一切正常,应该可以在当前目录下看到三个可执行文件,直接执行相应平台的二进制文件即可启动服务。?www.thinkjs.orggit:(master)npmrunpkg-build>thinkjs-official@1.2.0pkg-build/Users/lizheming/workspace/thinkjs/www.thinkjs.org>pkg./--out-path=dist>pkg@4.4.0?www.thinkjs.orggit:(master)?ls-alhdisttotal577096drwxr-xr-x5lizhemingstaff160B122817:35.drwxr-xr-x@30lizhemingstaff960B122817:34..-rwxr-xr-x1位lizheming员工87M122817:34thinkjs-official-linux-rwxr-xr-x1位lizheming员工87M122817:35thinkjs-official-macos-rw-r--r--1lizhemingstaff82M122817:35thinkjs-official-win.exe?www.thinkjs.orggit:(master)?项目打包后出现无法修改配置的问题。如果有dynamic需求,配置起来不是很方便。这里有两种解决这个问题的思路:将动态配置配置到环境变量中,程序通过读取环境变量来覆盖默认配置。使用ThinkJS提供的beforeStartServer()hook在启动前读取真实目录下的配置文件进行配置覆盖。//pkg.jsconstpath=require('path');think.beforeStartServer(()=>{constconfigFile=path.join(process.cwd(),'config.js');constconfig=require(configFile);think.config(config);});另外,随着项目复杂度的增加,业务中可能会引入大量的第三方模块。上一篇只是解决了ThinkJS项目本身动态导入的问题。如果导入的第三方模块也是动态导入的,也需要在pkg.assets配置中指定。另外,对于C++模块,pkg目前没有办法自动引入,也需要在pkg.assets中指定依赖资源。//package.json{"pkg":{"assets":[//以node-sqlite3模块为例"node_modules/sqlite3/lib/binding/node-v64-darwin-x64/node_sqlite3.node"]}其中node-v64-darwin-x64可能具有不同的名称,具体取决于平台。.node模块无法导入的原因是因为C++模块在安装的时候会通过node-gyp动态编译,这个操作是平台相关的。也就是说这个特性和pkg模块可以在一个平台上打包所有平台的二进制包的特性有冲突。毕竟pkg模块无法在Mac平台上编译Windows平台模块。所以这种情况下,除了手动导入编译好的.node模块外,还需要注意导入的.node模块和pkg.targets指定的编译平台的一致性。除了在对应的平台模块上安装.node模块外,还可以选择下载其他同学提供的编译好的模块。淘宝源码提供了很多二进制模块的编译结果。以node-sqlite3为例,其所有编译好的模块都可以在https://npm.taobao.org/mirror...下载,您可以选择对应的版本和平台。本文提到的打包配置在ThinkJS官网项目中已经实现。想尝试的同学可以直接clone官网项目。安装好依赖后,执行npmrunpkg-build,在dist/目录下获取二进制可执行文件。