:sparkles:🎨 Add project zip download and remove lib/app folder
This commit is contained in:
+1
-1
@@ -2,5 +2,5 @@
|
|||||||
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const App = require('../lib/app')
|
const App = require('../lib')
|
||||||
const app = new App()
|
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 opener = require('opener')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const packageJson = require('../../package.json')
|
const packageJson = require('../package.json')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* App class
|
* App class
|
||||||
@@ -186,9 +186,9 @@ class App
|
|||||||
// Start project
|
// Start project
|
||||||
socket.on('start_project', (data) =>
|
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
|
// Debug
|
||||||
if(this.config.debug >= 1)
|
if(this.config.debug >= 1)
|
||||||
@@ -214,6 +214,7 @@ class App
|
|||||||
socket.on('update_file', (data) =>
|
socket.on('update_file', (data) =>
|
||||||
{
|
{
|
||||||
project.files.createVersion(data.path, data.content)
|
project.files.createVersion(data.path, data.content)
|
||||||
|
project.zipNeedsUpdate = true
|
||||||
|
|
||||||
// Debug
|
// Debug
|
||||||
if(this.config.debug >= 1)
|
if(this.config.debug >= 1)
|
||||||
@@ -230,6 +231,7 @@ class App
|
|||||||
socket.on('create_file', (data) =>
|
socket.on('create_file', (data) =>
|
||||||
{
|
{
|
||||||
project.files.create(data.path, data.content)
|
project.files.create(data.path, data.content)
|
||||||
|
project.zipNeedsUpdate = true
|
||||||
|
|
||||||
// Debug
|
// Debug
|
||||||
if(this.config.debug >= 1)
|
if(this.config.debug >= 1)
|
||||||
@@ -246,6 +248,7 @@ class App
|
|||||||
socket.on('delete_file', (data) =>
|
socket.on('delete_file', (data) =>
|
||||||
{
|
{
|
||||||
project.files.delete(data.path)
|
project.files.delete(data.path)
|
||||||
|
project.zipNeedsUpdate = true
|
||||||
|
|
||||||
// Debug
|
// Debug
|
||||||
if(this.config.debug >= 1)
|
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
|
// Create project
|
||||||
const project = new Project(this.config, { name: _name })
|
const project = new Project(this.config, _options)
|
||||||
let sameNameProject = this.all[project.slug]
|
let sameNameProject = this.all[project.slug]
|
||||||
|
|
||||||
// Try to found same name project
|
// Try to found same name project
|
||||||
@@ -101,14 +101,33 @@ class Site
|
|||||||
/**
|
/**
|
||||||
* Create project
|
* 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.config = _config
|
||||||
this.path = process.cwd()
|
this.path = process.cwd()
|
||||||
|
|
||||||
|
this.setExcludeRegex()
|
||||||
this.setSocket()
|
this.setSocket()
|
||||||
this.watch()
|
this.watch()
|
||||||
}
|
}
|
||||||
@@ -34,7 +35,7 @@ class Watcher
|
|||||||
// Connect event
|
// Connect event
|
||||||
this.socket.on('connect', () =>
|
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
|
// Debug
|
||||||
if(this.config.debug)
|
if(this.config.debug)
|
||||||
@@ -54,13 +55,9 @@ class Watcher
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
setExcludeRegex()
|
||||||
* Watch
|
|
||||||
* Listen to modifications on files and folders
|
|
||||||
*/
|
|
||||||
watch()
|
|
||||||
{
|
{
|
||||||
// Create ignored regex
|
// Exclude files regex
|
||||||
const regexs = []
|
const regexs = []
|
||||||
const Minimatch = require('minimatch').Minimatch
|
const Minimatch = require('minimatch').Minimatch
|
||||||
|
|
||||||
@@ -77,14 +74,21 @@ class Watcher
|
|||||||
regexs.push(regex)
|
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
|
// Set up
|
||||||
this.watcher = chokidar.watch(
|
this.watcher = chokidar.watch(
|
||||||
this.path,
|
this.path,
|
||||||
{
|
{
|
||||||
// ignored: /[\/\\]\./,
|
// ignored: /[\/\\]\./,
|
||||||
ignored: regex,
|
ignored: this.excludeRegex,
|
||||||
ignoreInitial: !this.config.initialSend
|
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",
|
"main": "bin/index.js",
|
||||||
"scripts": {
|
"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"
|
"test-project": "node ./bin --debug 1 --test"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -21,10 +21,12 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/brunosimon/keppler",
|
"homepage": "https://github.com/brunosimon/keppler",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"adm-zip": "^0.4.7",
|
||||||
"chokidar": "^1.5.1",
|
"chokidar": "^1.5.1",
|
||||||
"colors": "^1.1.2",
|
"colors": "^1.1.2",
|
||||||
"diff": "^2.2.3",
|
"diff": "^2.2.3",
|
||||||
"express": "^4.13.4",
|
"express": "^4.13.4",
|
||||||
|
"glob": "^7.1.2",
|
||||||
"helmet": "^2.1.0",
|
"helmet": "^2.1.0",
|
||||||
"ip": "^1.1.3",
|
"ip": "^1.1.3",
|
||||||
"mime": "^1.3.4",
|
"mime": "^1.3.4",
|
||||||
|
|||||||
Reference in New Issue
Block a user