const Console = require("./console"); const fs = require("fs"); const request = require("./requests"); const semver = require("semver"); const path = require("path"); const http = require("http"); const https = require("https"); const targz = require('targz'); const config = require("./config")(); var console = new Console; module.exports = class PackageParser { load(path){ if(!path) path = process.cwd() + "/package.json"; if (!fs.existsSync(path)) throw Error("Package.json doesn't exists!"); try { var json = JSON.parse(fs.readFileSync(path)); } catch (e) { throw Error("Invalid JSON file"); } this.pkg = json; this.verify(); return json; } verify(){ var pkg = this.pkg; if(global.args.flags.supressChecking || global.args.flags.sch)return; if(!pkg.name)console.warn("No name present in package.json"); if(!pkg.description)console.warn("No description present in package.json") if(!pkg.repository)console.warn("Repository not specified") if(!pkg.license)console.warn("License not selected") if(!pkg.author)console.warn("Author not set") if(!pkg.version)console.warn("Version not set. Some commands may not work.") if(!pkg.keywords)console.warn("Add keywords to your package.json"); } async install(dev = false) { var deps = {}; Object.assign(deps, this.getDependencies()); if (dev) { Object.assign(deps, this.getDependencies(true));//merge devDependencies & dependencies } var dependencies = []; for (var peer in this.getPeerDependencies()) { console.warn("Peer dependency found. Install peer dependencies yourself: " + peer); } for (var dep in deps) { dependencies.push({ name: dep, version: deps[dep], type: "required" }) } var optional = this.getOptionalDependencies(); if (global.flags["no-optional"]) optional = {}; for (var dep in optional) { dependencies.push({ name: dep, version: optional[dep], type: "optional" }); } dependencies.forEach((dep) => { this.installDependency(dep.name, dep.version); }) } sanitizeName(pkg, version){ var targetPath = '.' + path.posix.normalize('/' + pkg) return path.posix.resolve(config.packages + "/../packages/", targetPath + "/" + version) } installDependency(pkg, version) { return new Promise(async(res, rej)=>{ console.time("installation time of " + pkg); var lock = {}; lock.version = 1; lock.packages = {}; var deps = await this.getRequiredPackages(pkg, version); var pk = deps.pkg; if (fs.existsSync(this.sanitizeName(pk.name, deps.version))) { console.log("Already installed in " + this.sanitizeName(pk.name, deps.version)); if (!fs.existsSync(process.cwd() + "/node_modules")) { fs.mkdirSync(process.cwd() + "/node_modules"); } if (!fs.existsSync(process.cwd() + "/node_modules/" + pk.name)) { if (path.dirname(process.cwd() + "/node_modules/" + pk.name) != process.cwd() + "/node_modules/") { if (!fs.existsSync(path.dirname(process.cwd() + "/node_modules/" + pk.name))) fs.mkdirSync(path.dirname(process.cwd() + "/node_modules/" + pk.name)); } console.log("Creating symlink"); fs.symlinkSync(path.relative(process.cwd() + "/node_modules/" + pk.name, this.sanitizeName(pk.name, deps.version)), process.cwd() + "/node_modules/" + pk.name); } if(!global.flags.f && !global.flags["force-install"]){ console.timeEnd("installation time of " + pkg); return res(); } } var d = deps.deps; if(d){ for(var v in d){ await this.installDependency(v, d[v]); } } if (!fs.existsSync(this.sanitizeName(pk.name, deps.version))) { //download console.log("Downloading " + pk.name + " from " + pk.dist.tarball + " to " + __dirname + "/../tars/" + path.basename(pk.dist.tarball)); await this.download(pk.dist.tarball, __dirname + "/../tars/" + path.basename(pk.dist.tarball)); targz.decompress({ src: __dirname + "/../tars/" + path.basename(pk.dist.tarball), dest: this.sanitizeName(pk.name, deps.version) }, (e) => { if(e)throw e; console.timeEnd("installation time of " + pkg); res(); }) } else { console.timeEnd("installation time of " + pkg); res(); } }) } download(from, to){ return new Promise((res, rej)=>{ if(fs.existsSync(to)){ return res(); } if (from.startsWith("http://")) { var handler = http; } else if (from.startsWith("https://")) { var handler = https; } else { throw Error("Unsupported protocol"); } const file = fs.createWriteStream(to); handler.get(from, (response) => { if (response.statusCode != 200) { throw Error("Got status code " + response.statusCode); } response.pipe(file); response.on('end', ()=>{ file.close(); if(!response.complete) throw Error("Couldn't download whole file"); res(); }) }); }); } getRequiredPackages(pkg, version){ return new Promise((res, rej)=>{ request(global.config.repository + pkg).then((o) => { var p = JSON.parse(o); if(p.error)return rej("Requested package couldn't be found."); var versions = []; for(var v in p.versions){ versions.push(v); } versions = versions.filter((val) => { return semver.satisfies(val, version); }); var ver = versions[versions.length - 1]; if(!ver){ throw Error("Cannot install - no supported version for package " + pkg + " that satisfies `" + version + "`"); } res({ deps: p.versions[ver].dependencies, version: ver, pkg: p.versions[ver] }); }).catch(rej); }) } getName(){ if(this.pkg.name)return this.pkg.name; throw Error("Name isn't specified in the package.json"); } getDescription() { if (this.pkg.description) return this.pkg.description; throw Error("Description isn't specified in the package.json"); } getDependencies(dev = false){ if(dev){ var deps = this.pkg.devDependencies; } else { var deps = this.pkg.dependencies; } if(!deps) return {}; return deps; } getPeerDependencies(){ if (this.pkg.peerDependencies) return this.pkg.peerDependencies; return {}; } getOptionalDependencies(){ if (this.pkg.optionalDependencies) return this.pkg.optionalDependencies; return {}; } }