:sparkles:🎨 Add project zip download and remove lib/app folder
This commit is contained in:
+1
-1
@@ -2,5 +2,5 @@
|
||||
|
||||
'use strict'
|
||||
|
||||
const App = require('../lib/app')
|
||||
const App = require('../lib')
|
||||
const app = new App()
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
// Dependencies
|
||||
const slug = require('slug')
|
||||
const Files = require('./files.js')
|
||||
|
||||
class Project
|
||||
{
|
||||
constructor(_config, _options)
|
||||
{
|
||||
this.config = _config
|
||||
|
||||
this.setName(_options.name)
|
||||
this.setSocket()
|
||||
|
||||
this.files = new Files(this.config, { projectSocket: this.projectSocket })
|
||||
this.date = new Date()
|
||||
}
|
||||
|
||||
setName(_name)
|
||||
{
|
||||
this.name = _name
|
||||
this.slug = slug(this.name, { lower: true })
|
||||
}
|
||||
|
||||
setSocket()
|
||||
{
|
||||
// Save original socket and create a channel for this specific project
|
||||
this.projectSocket = this.config.socket.of('/project/' + this.slug)
|
||||
|
||||
// Connection event
|
||||
this.projectSocket.on('connection', (socket) =>
|
||||
{
|
||||
this.projectSocket.emit('update_project', this.describe())
|
||||
|
||||
// Debug
|
||||
if(this.config.debug >= 1)
|
||||
{
|
||||
console.log('project > socket'.green.bold + ' - ' + 'connection'.cyan + ' - ' + socket.id.cyan)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
describe()
|
||||
{
|
||||
// Set up
|
||||
const result = {}
|
||||
|
||||
result.name = this.name
|
||||
result.files = this.files.describe()
|
||||
result.date = this.date
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
destructor()
|
||||
{
|
||||
this.projectSocket.emit('destruct')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Project
|
||||
@@ -1,224 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
class EventEmitter
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
constructor()
|
||||
{
|
||||
this.callbacks = {}
|
||||
this.callbacks.base = {}
|
||||
}
|
||||
|
||||
/**
|
||||
* On
|
||||
*/
|
||||
on(_names, callback)
|
||||
{
|
||||
const that = this
|
||||
|
||||
// Errors
|
||||
if(typeof _names === 'undefined' || _names === '')
|
||||
{
|
||||
console.warn('wrong names')
|
||||
return false
|
||||
}
|
||||
|
||||
if(typeof callback === 'undefined')
|
||||
{
|
||||
console.warn('wrong callback')
|
||||
return false
|
||||
}
|
||||
|
||||
// Resolve names
|
||||
const names = this.resolveNames(_names)
|
||||
|
||||
// Each name
|
||||
names.forEach(function(_name)
|
||||
{
|
||||
// Resolve name
|
||||
const name = that.resolveName(_name)
|
||||
|
||||
// Create namespace if not exist
|
||||
if(!(that.callbacks[ name.namespace ] instanceof Object))
|
||||
that.callbacks[ name.namespace ] = {}
|
||||
|
||||
// Create callback if not exist
|
||||
if(!(that.callbacks[ name.namespace ][ name.value ] instanceof Array))
|
||||
that.callbacks[ name.namespace ][ name.value ] = []
|
||||
|
||||
// Add callback
|
||||
that.callbacks[ name.namespace ][ name.value ].push(callback)
|
||||
})
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Off
|
||||
*/
|
||||
off(_names)
|
||||
{
|
||||
const that = this
|
||||
|
||||
// Errors
|
||||
if(typeof _names === 'undefined' || _names === '')
|
||||
{
|
||||
console.warn('wrong name')
|
||||
return false
|
||||
}
|
||||
|
||||
// Resolve names
|
||||
const names = this.resolveNames(_names)
|
||||
|
||||
// Each name
|
||||
names.forEach(function(_name)
|
||||
{
|
||||
// Resolve name
|
||||
const name = that.resolveName(_name)
|
||||
|
||||
// Remove namespace
|
||||
if(name.namespace !== 'base' && name.value === '')
|
||||
{
|
||||
delete that.callbacks[ name.namespace ]
|
||||
}
|
||||
|
||||
// Remove specific callback in namespace
|
||||
else
|
||||
{
|
||||
// Default
|
||||
if(name.namespace === 'base')
|
||||
{
|
||||
// Try to remove from each namespace
|
||||
for(const namespace in that.callbacks)
|
||||
{
|
||||
if(that.callbacks[ namespace ] instanceof Object && that.callbacks[ namespace ][ name.value ] instanceof Array)
|
||||
{
|
||||
delete that.callbacks[ namespace ][ name.value ]
|
||||
|
||||
// Remove namespace if empty
|
||||
if(Object.keys(that.callbacks[ namespace ]).length === 0)
|
||||
delete that.callbacks[ namespace ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Specified namespace
|
||||
else if(that.callbacks[ name.namespace ] instanceof Object && that.callbacks[ name.namespace ][ name.value ] instanceof Array)
|
||||
{
|
||||
delete that.callbacks[ name.namespace ][ name.value ]
|
||||
|
||||
// Remove namespace if empty
|
||||
if(Object.keys(that.callbacks[ name.namespace ]).length === 0)
|
||||
delete that.callbacks[ name.namespace ]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger
|
||||
*/
|
||||
trigger(_name, _args)
|
||||
{
|
||||
// Errors
|
||||
if(typeof _name === 'undefined' || _name === '')
|
||||
{
|
||||
console.warn('wrong name')
|
||||
return false
|
||||
}
|
||||
|
||||
const that = this
|
||||
let finalResult = null
|
||||
let result = null
|
||||
|
||||
// Default args
|
||||
const args = !(_args instanceof Array) ? [] : _args
|
||||
|
||||
// Resolve names (should on have one event)
|
||||
let name = this.resolveNames(_name)
|
||||
|
||||
// Resolve name
|
||||
name = this.resolveName(name[ 0 ])
|
||||
|
||||
// Default namespace
|
||||
if(name.namespace === 'base')
|
||||
{
|
||||
// Try to find callback in each namespace
|
||||
for(const namespace in that.callbacks)
|
||||
{
|
||||
if(that.callbacks[ namespace ] instanceof Object && that.callbacks[ namespace ][ name.value ] instanceof Array)
|
||||
{
|
||||
that.callbacks[ namespace ][ name.value ].forEach(function(callback)
|
||||
{
|
||||
result = callback.apply(that, args)
|
||||
|
||||
if(typeof finalResult === 'undefined')
|
||||
{
|
||||
finalResult = result
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Specified namespace
|
||||
else if(this.callbacks[ name.namespace ] instanceof Object)
|
||||
{
|
||||
if(name.value === '')
|
||||
{
|
||||
console.warn('wrong name')
|
||||
return this
|
||||
}
|
||||
|
||||
that.callbacks[ name.namespace ][ name.value ].forEach(function(callback)
|
||||
{
|
||||
result = callback.apply(that, args)
|
||||
|
||||
if(typeof finalResult === 'undefined')
|
||||
finalResult = result
|
||||
})
|
||||
}
|
||||
|
||||
return finalResult
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve names
|
||||
*/
|
||||
resolveNames(_names)
|
||||
{
|
||||
let names = _names
|
||||
names = names.replace(/[^a-zA-Z0-9 ,\/.]/g, '')
|
||||
names = names.replace(/[,\/]+/g, ' ')
|
||||
names = names.split(' ')
|
||||
|
||||
return names
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve name
|
||||
*/
|
||||
resolveName(name)
|
||||
{
|
||||
const newName = {}
|
||||
const parts = name.split('.')
|
||||
|
||||
newName.original = name
|
||||
newName.value = parts[ 0 ]
|
||||
newName.namespace = 'base' // Base namespace
|
||||
|
||||
// Specified namespace
|
||||
if(parts.length > 1 && parts[ 1 ] !== '')
|
||||
{
|
||||
newName.namespace = parts[ 1 ]
|
||||
}
|
||||
|
||||
return newName
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EventEmitter
|
||||
@@ -11,7 +11,7 @@ const colors = require('colors')
|
||||
const opener = require('opener')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const packageJson = require('../../package.json')
|
||||
const packageJson = require('../package.json')
|
||||
|
||||
/**
|
||||
* App class
|
||||
@@ -186,9 +186,9 @@ class App
|
||||
// Start project
|
||||
socket.on('start_project', (data) =>
|
||||
{
|
||||
project = this.projects.createProject(data.name)
|
||||
project = this.projects.createProject(data)
|
||||
|
||||
this.site.createProject(project.slug, data.path)
|
||||
this.site.createProjectRoute(project)
|
||||
|
||||
// Debug
|
||||
if(this.config.debug >= 1)
|
||||
@@ -214,6 +214,7 @@ class App
|
||||
socket.on('update_file', (data) =>
|
||||
{
|
||||
project.files.createVersion(data.path, data.content)
|
||||
project.zipNeedsUpdate = true
|
||||
|
||||
// Debug
|
||||
if(this.config.debug >= 1)
|
||||
@@ -230,6 +231,7 @@ class App
|
||||
socket.on('create_file', (data) =>
|
||||
{
|
||||
project.files.create(data.path, data.content)
|
||||
project.zipNeedsUpdate = true
|
||||
|
||||
// Debug
|
||||
if(this.config.debug >= 1)
|
||||
@@ -246,6 +248,7 @@ class App
|
||||
socket.on('delete_file', (data) =>
|
||||
{
|
||||
project.files.delete(data.path)
|
||||
project.zipNeedsUpdate = true
|
||||
|
||||
// Debug
|
||||
if(this.config.debug >= 1)
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
'use strict'
|
||||
|
||||
// Dependencies
|
||||
const slug = require('slug')
|
||||
const Files = require('./files.js')
|
||||
const AdmZip = require('adm-zip')
|
||||
const glob = require('glob')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
class Project
|
||||
{
|
||||
constructor(_config, _options)
|
||||
{
|
||||
this.config = _config
|
||||
|
||||
this.zipNeedsUpdate = true
|
||||
this.name = _options.name
|
||||
this.path = _options.path
|
||||
this.excludeRegex = new RegExp(_options.excludeRegex)
|
||||
this.slug = slug(this.name, { lower: true })
|
||||
|
||||
this.setSocket()
|
||||
|
||||
this.files = new Files(this.config, { projectSocket: this.projectSocket })
|
||||
this.date = new Date()
|
||||
}
|
||||
|
||||
setSocket()
|
||||
{
|
||||
// Save original socket and create a channel for this specific project
|
||||
this.projectSocket = this.config.socket.of('/project/' + this.slug)
|
||||
|
||||
// Connection event
|
||||
this.projectSocket.on('connection', (socket) =>
|
||||
{
|
||||
this.projectSocket.emit('update_project', this.describe())
|
||||
|
||||
// Debug
|
||||
if(this.config.debug >= 1)
|
||||
{
|
||||
console.log('project > socket'.green.bold + ' - ' + 'connection'.cyan + ' - ' + socket.id.cyan)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
getZipBuffer()
|
||||
{
|
||||
// Already zipped and don't need update
|
||||
if(!this.zipNeedsUpdate)
|
||||
{
|
||||
// Debug
|
||||
if(this.config.debug >= 1)
|
||||
{
|
||||
console.log('project > zip'.green.bold + ' - ' + 'from cache'.cyan)
|
||||
}
|
||||
|
||||
return this.zipBuffer
|
||||
}
|
||||
|
||||
// Search files with glob
|
||||
const files = glob.sync(this.path + '/**', { dot: true })
|
||||
|
||||
// Create a zip file
|
||||
const zip = new AdmZip()
|
||||
|
||||
// Add files to zip
|
||||
for(const _file of files)
|
||||
{
|
||||
const stats = fs.lstatSync(_file)
|
||||
|
||||
// Ignore if folder or exluded
|
||||
if(stats.isFile() && !_file.match(this.excludeRegex))
|
||||
{
|
||||
const basename = path.basename(_file)
|
||||
const relativePath = _file.replace(basename, '').replace(this.path, '')
|
||||
zip.addLocalFile(_file, relativePath)
|
||||
}
|
||||
}
|
||||
|
||||
// Create and return zip buffer
|
||||
this.zipBuffer = zip.toBuffer()
|
||||
this.zipNeedsUpdate = false
|
||||
|
||||
// Debug
|
||||
if(this.config.debug >= 1)
|
||||
{
|
||||
console.log('project > zip'.green.bold + ' - ' + 'create'.cyan)
|
||||
}
|
||||
|
||||
return this.zipBuffer
|
||||
}
|
||||
|
||||
describe()
|
||||
{
|
||||
// Set up
|
||||
const result = {}
|
||||
|
||||
result.name = this.name
|
||||
result.files = this.files.describe()
|
||||
result.date = this.date
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
destructor()
|
||||
{
|
||||
this.projectSocket.emit('destruct')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Project
|
||||
@@ -29,10 +29,10 @@ class Projects
|
||||
})
|
||||
}
|
||||
|
||||
createProject(_name)
|
||||
createProject(_options)
|
||||
{
|
||||
// Create project
|
||||
const project = new Project(this.config, { name: _name })
|
||||
const project = new Project(this.config, _options)
|
||||
let sameNameProject = this.all[project.slug]
|
||||
|
||||
// Try to found same name project
|
||||
@@ -101,14 +101,33 @@ class Site
|
||||
/**
|
||||
* Create project
|
||||
*/
|
||||
createProject(_slug, _path)
|
||||
createProjectRoute(_project)
|
||||
{
|
||||
this.express.get('/' + _slug, (request, response) =>
|
||||
// Project route
|
||||
this.express.get('/' + _project.slug, (request, response) =>
|
||||
{
|
||||
response.send('<a href="/' + _slug + '/files/empty.txt" download>download</a>')
|
||||
response.send(`
|
||||
<a href="/${_project.slug}/download" download>download project</a>
|
||||
<a href="/${_project.slug}/files/empty.txt" download>download file</a>
|
||||
`)
|
||||
})
|
||||
|
||||
this.express.use('/' + _slug + '/files', express.static(_path))
|
||||
// Project download route
|
||||
this.express.get('/' + _project.slug + '/download', (request, response) =>
|
||||
{
|
||||
// Get zip buffer
|
||||
const zipBuffer = _project.getZipBuffer()
|
||||
|
||||
// Send
|
||||
response.writeHead(200, {
|
||||
'Content-Type': 'application/zip',
|
||||
'Content-Length': zipBuffer.length
|
||||
})
|
||||
response.write(zipBuffer, 'binary')
|
||||
response.end(null, 'binary')
|
||||
})
|
||||
|
||||
this.express.use('/' + _project.slug + '/files', express.static(_project.path))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ class Watcher
|
||||
this.config = _config
|
||||
this.path = process.cwd()
|
||||
|
||||
this.setExcludeRegex()
|
||||
this.setSocket()
|
||||
this.watch()
|
||||
}
|
||||
@@ -34,7 +35,7 @@ class Watcher
|
||||
// Connect event
|
||||
this.socket.on('connect', () =>
|
||||
{
|
||||
this.socket.emit('start_project', { name: this.config.name, path: this.path })
|
||||
this.socket.emit('start_project', { name: this.config.name, path: this.path, excludeRegex: '' + this.excludeRegex })
|
||||
|
||||
// Debug
|
||||
if(this.config.debug)
|
||||
@@ -54,13 +55,9 @@ class Watcher
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch
|
||||
* Listen to modifications on files and folders
|
||||
*/
|
||||
watch()
|
||||
setExcludeRegex()
|
||||
{
|
||||
// Create ignored regex
|
||||
// Exclude files regex
|
||||
const regexs = []
|
||||
const Minimatch = require('minimatch').Minimatch
|
||||
|
||||
@@ -77,14 +74,21 @@ class Watcher
|
||||
regexs.push(regex)
|
||||
}
|
||||
|
||||
const regex = new RegExp(regexs.join('|'))
|
||||
this.excludeRegex = new RegExp(regexs.join('|'))
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch
|
||||
* Listen to modifications on files and folders
|
||||
*/
|
||||
watch()
|
||||
{
|
||||
// Set up
|
||||
this.watcher = chokidar.watch(
|
||||
this.path,
|
||||
{
|
||||
// ignored: /[\/\\]\./,
|
||||
ignored: regex,
|
||||
ignored: this.excludeRegex,
|
||||
ignoreInitial: !this.config.initialSend
|
||||
}
|
||||
)
|
||||
Generated
+1336
-207
File diff suppressed because it is too large
Load Diff
+3
-1
@@ -7,7 +7,7 @@
|
||||
},
|
||||
"main": "bin/index.js",
|
||||
"scripts": {
|
||||
"demo-folder": "cd test/demo-folder && node ../../bin 'Demo folder' --debug 1 -i",
|
||||
"demo-folder": "cd test/demo-folder && node ../../bin 'Demo folder'",
|
||||
"test-project": "node ./bin --debug 1 --test"
|
||||
},
|
||||
"repository": {
|
||||
@@ -21,10 +21,12 @@
|
||||
},
|
||||
"homepage": "https://github.com/brunosimon/keppler",
|
||||
"dependencies": {
|
||||
"adm-zip": "^0.4.7",
|
||||
"chokidar": "^1.5.1",
|
||||
"colors": "^1.1.2",
|
||||
"diff": "^2.2.3",
|
||||
"express": "^4.13.4",
|
||||
"glob": "^7.1.2",
|
||||
"helmet": "^2.1.0",
|
||||
"ip": "^1.1.3",
|
||||
"mime": "^1.3.4",
|
||||
|
||||
Reference in New Issue
Block a user