Skip to content

Commit

Permalink
feat: create esbuild bundle for embedded devices (#3480)
Browse files Browse the repository at this point in the history
  • Loading branch information
robertsLando authored Dec 12, 2023
1 parent 932495e commit 68326d6
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 3 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,6 @@ typings/
!.yarn/sdks
!.yarn/versions

fakeNodes.json
fakeNodes.json

/build/
2 changes: 1 addition & 1 deletion api/lib/ZwaveClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4400,7 +4400,7 @@ class ZwaveClient extends TypedEventEmitter<ZwaveClientEventCallbacks> {
controllerNode.statistics as ControllerStatistics
controllerNode.statistics = stats

if (stats.messagesRX > oldStatistics?.messagesRX ?? 0) {
if (stats.messagesRX > (oldStatistics?.messagesRX ?? 0)) {
// no need to emit `lastActive` event. That would cause useless traffic
controllerNode.lastActive = Date.now()
}
Expand Down
4 changes: 3 additions & 1 deletion api/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ export function fileDate(date?: Date) {
}

/** Where package.json is */
export const basePath = resolve(__dirname, '..', '..')
export const basePath = __filename.endsWith('index.js')
? resolve(__dirname) // esbuild bundle
: resolve(__dirname, '..', '..')

/**
* Get the base root path to application directory. When we are in a `pkg` environment
Expand Down
138 changes: 138 additions & 0 deletions esbuild.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
const esbuild = require('esbuild')
const { cp, stat, readFile, writeFile } = require('fs/promises')
const { exists, emptyDir } = require('fs-extra')

const outputDir = 'build'

// from https://github.com/evanw/esbuild/issues/1051#issuecomment-806325487
const nativeNodeModulesPlugin = {
name: 'native-node-modules',
setup(build) {
// If a ".node" file is imported within a module in the "file" namespace, resolve
// it to an absolute path and put it into the "node-file" virtual namespace.
build.onResolve({ filter: /\.node$/, namespace: 'file' }, (args) => ({
path: require.resolve(args.path, { paths: [args.resolveDir] }),
namespace: 'node-file',
}))

// Files in the "node-file" virtual namespace call "require()" on the
// path from esbuild of the ".node" file in the output directory.
build.onLoad({ filter: /.*/, namespace: 'node-file' }, (args) => ({
contents: `
import path from ${JSON.stringify(args.path)}
try { module.exports = require(path) }
catch {}
`,
}))

// If a ".node" file is imported within a module in the "node-file" namespace, put
// it in the "file" namespace where esbuild's default loading behavior will handle
// it. It is already an absolute path since we resolved it to one above.
build.onResolve(
{ filter: /\.node$/, namespace: 'node-file' },
(args) => ({
path: args.path,
namespace: 'file',
}),
)

// Tell esbuild's default loading behavior to use the "file" loader for
// these ".node" files.
let opts = build.initialOptions
opts.loader = opts.loader || {}
opts.loader['.node'] = 'file'
},
}

async function printSize(fileName) {
const stats = await stat(fileName)

// print size in MB
console.log(`Bundle size: ${Math.round(stats.size / 10000) / 100}MB\n\n`)
}

async function main() {
const start = Date.now()
// clean build folder
await emptyDir(outputDir)

const outfile = `${outputDir}/index.js`

const externals = [
'@serialport/bindings-cpp/prebuilds',
'zwave-js/package.json',
'@zwave-js/config/package.json',
'@zwave-js/config/config',
'@zwave-js/config/build',
'./snippets',
'./dist',
]

/** @type { import('esbuild').BuildOptions } */
const config = {
entryPoints: ['api/bin/www.ts'],
plugins: [nativeNodeModulesPlugin],
bundle: true,
platform: 'node',
target: 'node18',
sourcemap: process.argv.includes('--sourcemap'),
outfile,
// suppress direct-eval warning
logOverride: {
'direct-eval': 'silent',
},
external: externals,
}

await esbuild.build(config)

console.log(`Build took ${Date.now() - start}ms`)
await printSize(outfile)

const content = (await readFile(outfile, 'utf-8'))
.replace(
/__dirname, "\.\.\/"/g,
'__dirname, "./node_modules/@serialport/bindings-cpp"',
)
.replace(
`__dirname, "../package.json"`,
`__dirname, "./node_modules/@zwave-js/config/package.json"`,
)
.replace(
`__dirname, "../config"`,
`__dirname, "./node_modules/@zwave-js/config/config"`,
)

await writeFile(outfile, content)

if (process.argv.includes('--minify')) {
// minify the file
await esbuild.build({
...config,
entryPoints: [outfile],
minify: true,
keepNames: true, // needed for zwave-js as it relies on class names
allowOverwrite: true,
outfile,
})

console.log(`Minify took ${Date.now() - start}ms`)
await printSize(outfile)
}

// copy assets to build folder
for (const ext of externals) {
const path = ext.startsWith('./') ? ext : `node_modules/${ext}`
if (await exists(path)) {
console.log(`Copying "${path}" to "${outputDir}" folder`)
await cp(path, `${outputDir}/${path}`, { recursive: true })
} else {
console.log(`Asset "${path}" does not exist. Skipping...`)
}
}
}

main().catch((err) => {
console.error(err)
process.exit(1)
})
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"server": "ts-node -r esbuild-register api/bin/www.ts",
"fake-stick": "npm run mock-server -c server_config.js",
"start": "node --preserve-symlinks server/bin/www.js",
"bundle": "node esbuild.js",
"lint": "npm-run-all 'lint:*'",
"lint-fix": "npm-run-all 'lint-fix:*'",
"lint:eslint": "eslint --ext .js,.ts,.vue .",
Expand Down

0 comments on commit 68326d6

Please sign in to comment.