wip: bundling work

This commit is contained in:
calebboyd
2017-07-05 23:18:08 -05:00
parent ac64063916
commit ce2c4cefd0
7 changed files with 135 additions and 56 deletions
+8 -46
View File
@@ -1,53 +1,15 @@
import Mfs from 'memory-fs'
import webpack from 'webpack'
import {
FuseBox,
CSSPlugin,
JSONPlugin,
HTMLPlugin } from 'fuse-box'
import { fromCallback } from 'bluebird'
import { resolve, join, relative } from 'path'
const isObject = (x) => typeof x === 'object'
const isString = (x) => x instanceof String || typeof x === 'string'
import * as path from 'path'
export default async function bundle (compiler, next) {
let bundleConfig = compiler.options.bundle
if (!bundleConfig) {
let options = compiler.options.bundle
if (!options) {
return next()
}
const inputFilePath = compiler.options.input || require.resolve(process.cwd())
const mfs = new Mfs()
const path = resolve('nexe')
const filename = 'virtual-bundle.js'
if (isString(bundleConfig)) {
bundleConfig = require(relative(process.cwd(), bundleConfig))
} else if (!isObject(bundleConfig)) {
bundleConfig = {
entry: resolve(inputFilePath),
target: 'node',
module: {
rules: [
{ test: /\.html$/, use: 'html-loader' },
{ test: /\.json$/, use: 'json-loader' },
{ test: /\.node$/, use: 'xbin-loader' },
{ test: /license$|\.md$/, use: 'raw-loader' }
]
}
}
}
bundleConfig.output = { path, filename }
const bundler = webpack(bundleConfig)
bundler.outputFileSystem = mfs
const stats = await fromCallback(cb => bundler.run(cb))
if (stats.hasErrors()) {
// compiler.log.error(stats.toString())
return null
}
compiler.input = mfs.readFileSync(
join(bundleConfig.output.path, bundleConfig.output.filename)
)
return next()
}
+91
View File
@@ -0,0 +1,91 @@
import * as fs from 'fs'
import * as path from 'path'
import * as child from 'child_process'
function findNativeModulePath (filePath, bindingsArg) {
const dirname = path.dirname(filePath)
const tempFile = Math.random() * 100 + '.js'
const tempFilePath = path.join(dirname, tempFile)
fs.writeFileSync(tempFilePath, `
var bindings = require('bindings');
var Module = require('module');
var originalRequire = Module.prototype.require;
Module.prototype.require = function(path) {
const mod = originalRequire.apply(this, arguments);
process.stdout.write(path)
return mod
};
bindings('${bindingsArg}')
`)
//using exec because it can be done syncronously
const nativeFileName = child.execSync('node ' + tempFile, { cwd: dirname }).toString()
fs.unlinkSync(tempFilePath)
const relativePath = './' + path.relative(dirname, nativeFileName).replace(/\\/g, '/')
return relativePath
}
/**
* Traverse all nodes in a file and evaluate usages of the bindings module
*/
export class BindingsRewrite {
constructor () {
this.bindingsIdNodes = []
this.nativeModulePaths = []
this.rewrite = false
}
isRequireBindings (node) {
return node.callee.name === 'require' && node.arguments[0].value === 'bindings'
}
onNode (absolutePath, node, parent) {
if (node.type === 'CallExpression') {
if (this.isRequireBindings(node) && parent.type === 'VariableDeclarator') {
/**
* const loadBindings = require('bindings');
* -> const loadBindings = String('');
*/
this.bindingsIdNodes.push(parent.id)
node.callee.name = 'String'
node.arguments[0].value = ''
this.rewrite = true
return
}
if (this.isRequireBindings(node) && parent.type === 'CallExpression') {
/**
*const bindings = require('bindings')('native-module')....
* -> const bindings = require('./path/to/native/module.node').....
*/
const bindingsArgNode = parent.arguments[0]
if (bindingsArgNode.type === 'Literal') {
parent.callee = { type: 'Identifier', name: 'require' }
bindingsArgNode.value = findNativeModulePath(absolutePath, bindingsArgNode.value)
this.nativeModulePaths.push(bindingsArgNode.value)
this.rewrite = true
return
}
}
const bindingsInvocationIdx = this.bindingsIdNodes.findIndex(x => node.callee.name === x.name)
if (this.bindingsIdNodes[bindingsInvocationIdx]) {
/**
* const bindings = loadBindings('native-module')
* -> const bindings = require('./path/to/native/module.node')
*/
const bindingsIdNode = this.bindingsIdNodes[bindingsInvocationIdx]
const bindingsArgNode = node.arguments[0]
this.bindingsIdNodes.splice(bindingsInvocationIdx, 1)
if (bindingsArgNode.type === 'Literal') {
node.callee.name = 'require'
bindingsArgNode.value = findNativeModulePath(absolutePath, bindingsArgNode.value)
this.nativeModulePaths.push(bindingsArgNode.value)
this.rewrite = true
return
}
}
}
}
}
+30
View File
@@ -0,0 +1,30 @@
import { BindingsRewrite } from './bindings-rewrite'
export class NativeModulePlugin {
constructor (...moduleNames) {
this.test = new RegExp(`node_modules\/(${moduleNames.join('|')}).*\.js`)
this.limit2project = false
}
transform (file) {
const bindingsRewrite = new BindingsRewrite()
file.loadContents()
file.makeAnalysis(null, {
plugins: [{
onNode(file, node, parent) {
bindingsRewrite.onNode(file.info.absPath, node, parent)
},
onEnd(file) {
if (bindingsRewrite.rewrite) {
const index = file.analysis.dependencies.indexOf('bindings')
if (~index) {
file.analysis.dependencies.splice(index, 1)
}
file.analysis.dependencies.push(...bindingsRewrite.nativeModulePaths)
file.analysis.requiresRegeneration = true
}
}
}]
})
}
}