ack-webpack
Version:
A code bundler that drastically reduces setup time by offering an init prompt of project setup questions and includes a fantastic browser reloader.
246 lines (193 loc) • 6.57 kB
JavaScript
const ackPath = require('ack-path')
const ackP = require('ack-p')
const fs = require('fs')
const path = require('path')
const log = require('../log.function')
const install = require('../install.function')
const rootPackPath = process.cwd()
class SubInstall{
constructor(packPath, config={originalPackage:null, lock:false, dev:false}){
this.packPath = packPath
this.config = config
}
discoverPackPathBy(pathing, original){
original = original || pathing
const packPath = path.join(pathing,'package.json')
const exists = fs.existsSync( packPath )
if(exists)return packPath
if(this.packPath.split(path.sep).length==pathing.split(path.sep)){
throw new Error('Could not find package.json by path: '+original)
}
return this.discoverPackPathBy( path.join(pathing,'../'), original )
}
isLockMode(){
return this.config.lock
}
getDepKey(){
return this.config.depKey || 'jsDependencies'
}
getPackPath(){
return this.discoverPackPathBy(this.packPath)
}
getPack(){
if( this.packageJson )return this.packageJson;
return this.packageJson = this.fetchPack()
}
fetchPack(){
return JSON.parse( fs.readFileSync( this.getPackPath() ).toString() )
}
getInstalls(){
return this.getPack()[ this.getDepKey() ]
}
performInstalls(name, version){
return this.performInstallsBy( this.getInstalls() )
}
performInstallBy(name, version){
const ob = {}
ob[name] = version
return this.performInstallsBy(ob)
}
performInstallsBy(installs){
let promise = Promise.resolve()
if(!installs)return promise
// const installArray = []
// const subInstalls = []
Object.keys(installs).forEach(name=>{
const versionDef = installs[name] ? '@' + installs[name] : ''
const installDef = name + versionDef
let resolvePath = ''
if(this.config.prefix){
resolvePath = path.join(process.cwd(), this.config.prefix, 'node_modules', name)
}else{
resolvePath = path.join(process.cwd(), 'node_modules', name)
}
const config = Object.assign({dev:this.config.originalPackage}, this.config)
const subConfig = Object.assign({}, this.config)
subConfig.originalPackage = false
const packName = installCommandToPackageName(name)
promise = promise.then( ()=>install(installDef, config) )
.then( config=>this.saveName(name, installs[name]) )
.then( ()=>new SubInstall( require.resolve(packName), subConfig) )
.then( SubInstall=>SubInstall.performInstalls() )
})
return promise
}
saveName(name, version){
if(!this.config.lock || !this.config.originalPackage){
return name
}
const pack = this.fetchPack()//always a fresh copy incase background changes like npm modified the package.json file
const key = this.getDepKey()
pack[ key ] = pack[ key ] || {}
pack[ key ][ name ] = version
this.savePack( pack )
}
saveInstallByName(name){
const vSplit = name.split('/').pop().split('@')
let nameOnly = []
if(vSplit.length>1){
nameOnly = name.split('@')
nameOnly.pop()
let version = vSplit.pop()
return this.saveInstallBy(nameOnly.join('@'), version)
}
//non-npm install
if( !isNpmSiteInstall(name) ){
nameOnly = name.split('/')
return this.saveInstallBy(nameOnly.pop(), name)
}
return install.promiseVersion(name)
.then(version=>this.saveInstallBy(name,version))
}
saveInstallBy(name, version){
const pack = this.getPack()
const key = this.getDepKey()
pack[key] = pack[key] || {}
pack[key][ name ] = (version || name)
}
savePack(pack){
pack = pack || this.getPack()
const packPath = this.discoverPackPathBy(this.packPath)
const data = JSON.stringify(pack, null, 2)
fs.writeFileSync(packPath, data)
}
}
module.exports.exec = function(args){
args = args || process.argv
const depKeyArgIndex = args.indexOf('--depkey')
const subInstall = new SubInstall(rootPackPath,{
lock: args.indexOf('--lock')>=0,
originalPackage: true,
depKey: depKeyArgIndex>=0 ? args[ depKeyArgIndex+1 ] : 'jsDependencies'
})
let promise = ackP.resolve()
log('Reading Package', subInstall.getPackPath())
let out = ''
const outArgIndex = args.indexOf('--out')
if(outArgIndex>=0){
out = args[ outArgIndex+1 ]
}
const RootPath = ackPath(rootPackPath).join('.ack-webpack-temp')
const MoveTo = ackPath(rootPackPath).join(out)
if(out){
subInstall.config.prefix = './.ack-webpack-temp'
const fooPack = '{"description":"...", "repository":"...", "license":"UNLICENSED"}'
const TempNodeModules = RootPath.Join('node_modules')
promise = promise.then( ()=>RootPath.param() )
.then( ()=>RootPath.File('package.json').param(fooPack) )
.then( ()=>MoveTo.moveTo( TempNodeModules ) )//maybe install folder exists?
.catch('ENOENT', e=>null)
}
const requestedInstalls = getRequestedInstalls(args)
if( requestedInstalls.length ){
requestedInstalls.forEach(name=>{
if( isNpmSiteInstall(name) ){
promise = promise.then( ()=>install.promiseVersion(name) )
promise = promise.then( version=>subInstall.performInstallBy(name, version) )
}else{
promise = promise.then( version=>subInstall.performInstallBy(name) )
}
promise = promise.then( ()=>subInstall.saveInstallByName(name) )
//promise = promise.then( ()=>subInstall.savePack() )
})
const save = args.indexOf('--no-save') < 0
if( save ){
promise = promise.then( ()=>subInstall.savePack() )
}
}else{
promise = promise.then( ()=>subInstall.performInstalls() )
}
if(out){
promise = promise
.then( ()=>MoveTo.param() )
.then( ()=>RootPath.Join('node_modules').moveTo(MoveTo.path,true) )
.then( ()=>RootPath.delete() )
}
promise.catch( e=>log.error(e) )
}
function isNpmSiteInstall(name){
return name.search(/^[^:]+:/) >= 0 ? false : true
}
function installCommandToPackageName(name){
let cut = []
if( name.match(':') ){
cut = name.split(':')
cut.shift()//remove github
name = cut.join(':')
if( name.match('/') ){
cut = name.split('/')
cut.shift()
name = cut.join('/')
}
}
return name
}
function getRequestedInstalls(args){
const requestedInstalls = []
for(let x=3; x < args.length; ++x){
if(args[x].substring(0, 2)=='--')break;
requestedInstalls.push( args[x] )
}
return requestedInstalls
}