Skip to content

Commit

Permalink
feat: integrate lock
Browse files Browse the repository at this point in the history
  • Loading branch information
watilde committed Sep 15, 2020
1 parent e21dc59 commit c0e4eba
Show file tree
Hide file tree
Showing 9 changed files with 304 additions and 79 deletions.
14 changes: 10 additions & 4 deletions lib/lock.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
const path = require('path')
const resolver = require('./lock/resolver')
const locker = require('./lock/locker')
const dep = require('../package')

global.dependenciesTree = {}

Expand All @@ -11,15 +10,22 @@ const lock = (argv) => {
const lock = {
name: pkgJSON.name,
version: pkgJSON.version,
depVersion: dep.version
lockfileVersion: 1
}
const list = resolver()
var deps = Object.assign(
{},
pkgJSON.optionalDependencies || {},
pkgJSON.devDependencies || {},
pkgJSON.dependencies || {}
)

const list = resolver(deps)
process.stdout.write('Resolving dependencies\n')
Promise.all(list).then(() => {
lock.dependencies = global.dependenciesTree
locker(lock)
process.stdout.write(
'created node_modules.json\n'
'created package-lock.json\n'
)
}).catch((e) => { process.stderr.write(e.stack) })
}
Expand Down
2 changes: 1 addition & 1 deletion lib/lock/locker.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const writeFileSync = require('fs').writeFileSync
const path = require('path')
const pkgLockJSON = path.join(process.cwd(), 'node_modules.json')
const pkgLockJSON = path.join(process.cwd(), 'package-lock.json')

const locker = (pkg) => {
writeFileSync(pkgLockJSON, JSON.stringify(pkg, 2, 2))
Expand Down
114 changes: 40 additions & 74 deletions lib/lock/resolver.js
Original file line number Diff line number Diff line change
@@ -1,84 +1,50 @@
const request = require('request')
const path = require('path')
const fs = require('fs')
const readdirSync = require('fs').readdirSync
const npmrc = require('../utils/npmrc')
const nm = require('../utils/nm')
const semver = require('semver')
const tree = require('./resolver/tree')
const fetcher = require('./resolver/fetcher')

const resolver = (dep, base, resolve, reject) => {
base = base || nm
if (dep.startsWith('@') && dep.indexOf('/') === -1) {
const dir = path.join(base, dep)
if (fs.existsSync(dir)) {
const deps = readdirSync(dir).filter((n) => {
return n[0] !== '.'
}).map((n) => {
return dep + '/' + n
})
const tasks = deps.map((dep) => {
return new Promise((resolve, reject) => resolver(dep, base, resolve, reject))
})
Promise.all(tasks).then(() => {
resolve()
}).catch(reject)
} else {
resolve()
const resolver = (dep, deps, base, resolve, reject) => {
// Search for all related module files referenced by require
if (global.dependenciesTree[dep] && global.dependenciesTree[dep].version) {
if (semver.satisfies(global.dependenciesTree[dep].version, deps[dep])) {
return resolve()
}
return
}
const pkgJSON = require(path.join(base, dep, 'package.json'))
const options = {
url: npmrc.registry + pkgJSON.name.replace('/', '%2f') + '/v' + pkgJSON.version,
headers: {
'User-Agent': npmrc.userAgent
for (var i = 0; base.length > i; i += 2) {
var target = tree.getter(global.dependenciesTree, base.slice(0, i))
if (target && target[dep] && target[dep].version) {
if (semver.satisfies(target[dep].version, deps[dep])) {
return resolve()
}
}
}
var body = ''
request.get(options)
.on('data', (chunk) => { body += chunk })
.on('end', () => {
try {
body = JSON.parse(body)
} catch (e) { reject(e) }
if (base === nm) {
global.dependenciesTree[pkgJSON.name] = {
version: body.version,
tarball: body.dist.tarball,
shasum: body.dist.shasum
}
} else {
base = base.split(path.sep).slice(-2)[0]
if (!global.dependenciesTree[base].dependencies) {
global.dependenciesTree[base].dependencies = {}
}
global.dependenciesTree[base].dependencies[dep] = {}
global.dependenciesTree[base].dependencies[dep].version = body.version
global.dependenciesTree[base].dependencies[dep].tarball = body.dist.tarball
global.dependenciesTree[base].dependencies[dep].shasum = body.dist.shasum
}
const dir = path.join(base, dep, 'node_modules')
if (fs.existsSync(dir)) {
const deps = readdirSync(dir).filter((n) => {
return n[0] !== '.'
})
const tasks = deps.map((dep) => {
return new Promise((resolve, reject) => resolver(dep, dir, resolve, reject))
})
Promise.all(tasks).then(() => {
resolve()
}).catch(reject)
} else {
resolve()
}
fetcher(dep, deps[dep]).then((pkg) => {
const item = Object.assign({}, pkg)
delete item.dependencies
if (!global.dependenciesTree[dep]) {
global.dependenciesTree[dep] = item
} else {
tree.setter(global.dependenciesTree, dep, item, base)
}

if (!pkg.dependencies) return resolve()

const tasks = Object.keys(pkg.dependencies).map((item) => {
const list = pkg.dependencies
var keys = base.length === 0
? [].concat(`${dep}@${pkg.version}`)
: [].concat(base, 'dependencies', `${dep}@${pkg.version}`)
return new Promise((resolve, reject) => {
resolver(item, list, keys, resolve, reject)
})
})
.on('error', reject)
Promise.all(tasks).then(() => {
resolve()
}).catch(reject)
}).catch(reject)
}

module.exports = () => {
const deps = readdirSync(nm).filter((n) => {
return n[0] !== '.'
})
return deps.map((dep) => {
return new Promise((resolve, reject) => resolver(dep, null, resolve, reject))
module.exports = (deps) => {
return Object.keys(deps).map((dep) => {
return new Promise((resolve, reject) => resolver(dep, deps, [], resolve, reject))
})
}
29 changes: 29 additions & 0 deletions lib/lock/resolver/fetcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const npa = require('npm-package-arg')
const git = require('./fetchers/git')
const local = require('./fetchers/local')
const remote = require('./fetchers/remote')
const registry = require('./fetchers/registry')

module.exports = (name, spec) => {
const pkg = spec ? `${name}@${spec}` : name
const result = npa(pkg, process.cwd())

switch (result.type) {
// type git
case 'git':
return git(name, result.fetchSpec, result)
// type remote
case 'remote':
return remote(name, result.fetchSpec, result)
// type local
case 'file':
case 'directory':
return local(name, result.fetchSpec, result)
// type registry
case 'tag':
case 'range':
case 'version':
default:
return registry(name, result.fetchSpec, result)
}
}
76 changes: 76 additions & 0 deletions lib/lock/resolver/fetchers/git.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
const request = require('request')
const npmrc = require('../../../utils/npmrc')
const git = require('../../../utils/git')
const semver = require('semver')

const filetemplate = (hosted) => {
return hosted.filetemplate
.replace('{auth@}', hosted.auth ? hosted.auth + '@' : '')
.replace('{domain}', hosted.domain)
.replace('{user}', hosted.user)
.replace('{project}', hosted.project)
.replace('{committish}', hosted.committish ? hosted.committish : '')
.replace('{path}', 'package.json')
}

const httpstemplate = (hosted, committish) => {
return hosted.httpstemplate
.replace('git+https', 'https')
.replace('{auth@}', hosted.auth ? hosted.auth + '@' : '')
.replace('{domain}', hosted.domain)
.replace('{user}', hosted.user)
.replace('{project}', hosted.project)
.replace('{#committish}', committish ? '#' + hosted.committish : '')
}

module.exports = (name, spec, result) => {
var hosted = result.hosted
return new Promise((resolve, reject) => {
if (!hosted.committish) {
hosted.committish = git.sync(['ls-remote', httpstemplate(hosted), 'HEAD'])
.slice(0, 6)
} else if (hosted.committish.startsWith('semver:')) {
const range = hosted.committish.replace('semver:', '')
const list = git.sync(['ls-remote', '--tags', httpstemplate(hosted)])
.split('\n')
list.forEach((str) => {
if (str.split('\t').length === 1) return
const hash = str.split('\t')[0].slice(0, 6)
const version = str.split('\t')[1].replace('refs/tags/', '')
if (version.match(/\^\{\}/)) return
if (!semver.valid(version)) return
if (semver.satisfies(version, range)) hosted.committish = hash
})
} else {
const committish = git.sync(['ls-remote', '--tags', httpstemplate(hosted)])
.split('\n')
.filter((str, i) => {
return str.indexOf(hosted.committish) !== -1
}).pop()
hosted.committish = committish
? committish.split('\t')[0]
: hosted.committish
}
const options = {
url: filetemplate(hosted),
headers: {
'User-Agent': npmrc.userAgent
}
}
var body = ''
request.get(options)
.on('data', (chunk) => { body += chunk })
.on('end', () => {
try {
body = JSON.parse(body)
resolve({
type: 'git',
version: body.version,
dependencies: body.dependencies,
url: httpstemplate(hosted, true)
})
} catch (e) { return reject(e) }
})
.on('error', reject)
})
}
18 changes: 18 additions & 0 deletions lib/lock/resolver/fetchers/local.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const path = require('path')

module.exports = (name, spec, result) => {
const pkgJSON = spec.indexOf('package.json') !== -1
? spec
: path.join(spec, 'package.json')
return new Promise((resolve, reject) => {
try {
const pkg = require(pkgJSON)
resolve({
type: 'local',
version: pkg.version,
dependencies: pkg.dependencies,
url: spec
})
} catch (e) { reject(e) }
})
}
41 changes: 41 additions & 0 deletions lib/lock/resolver/fetchers/registry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const request = require('request')
const semver = require('semver')
const npmrc = require('../../../utils/npmrc')

module.exports = (name, spec, result) => {
return new Promise((resolve, reject) => {
const options = {
url: npmrc.registry + result.escapedName,
headers: {
'User-Agent': npmrc.userAgent
}
}
var body = ''
request.get(options)
.on('data', (chunk) => { body += chunk })
.on('end', () => {
try {
body = JSON.parse(body)
const versions = Object.keys(body.versions)
const version = versions.reduce((accumulator, currentValue) => {
if (semver.satisfies(accumulator, spec)) return accumulator
return currentValue
}, '')
const target = body.versions[version]
if (target.deprecated) {
process.stdout.write(
`${target.name}@${target.version}: ${target.deprecated}\n\n`
)
}
resolve({
type: 'registry',
version: target.version,
dependencies: target.dependencies,
tarball: target.dist.tarball,
shasum: target.dist.shasum
})
} catch (e) { return reject(e) }
})
.on('error', reject)
})
}
43 changes: 43 additions & 0 deletions lib/lock/resolver/fetchers/remote.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const request = require('request')
const tar = require('tar-stream')
const gunzip = require('gunzip-maybe')
const npmrc = require('../../../utils/npmrc')

module.exports = (name, spec, result) => {
const options = {
url: spec,
headers: {
'User-Agent': npmrc.userAgent
}
}
return new Promise((resolve, reject) => {
const extract = tar.extract()
var data = ''
extract.on('entry', (header, stream, cb) => {
const file = header.name.split('/').pop()
stream.on('data', (chunk) => {
if (file === 'package.json') data += chunk
})
stream.on('end', () => {
if (data) {
try {
const pkgJSON = JSON.parse(data)
resolve({
type: 'remote',
version: pkgJSON.version,
dependencies: pkgJSON.dependencies,
url: spec
})
} catch (e) { reject(e) }
} else {
cb()
}
})
stream.resume()
})
request.get(options)
.pipe(gunzip())
.pipe(extract)
.on('error', reject)
})
}
Loading

0 comments on commit c0e4eba

Please sign in to comment.