Skip to content

Commit

Permalink
feat(command): [WIP] add 'addFromNpm'
Browse files Browse the repository at this point in the history
  • Loading branch information
neikvon committed Dec 15, 2020
1 parent 1be5035 commit 5fc08d0
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 89 deletions.
9 changes: 4 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@
"url": "https://github.com/fbi-js/fbi/issues"
},
"dependencies": {
"@types/fs-extra": "^9.0.4",
"@types/fs-extra": "^9.0.5",
"chalk": "^4.1.0",
"clean-stack": "^3.0.1",
"commander": "6.2.0",
"commander": "6.2.1",
"enquirer": "^2.3.6",
"execa": "^4.1.0",
"execa": "^5.0.0",
"fs-extra": "^9.0.1",
"ora": "^5.1.0",
"pkg-dir": "^5.0.0",
"semver": "^7.3.2",
"semver": "^7.3.4",
"tiny-glob": "^0.2.8"
},
"devDependencies": {
Expand All @@ -54,7 +54,6 @@
"@typescript-eslint/parser": "^4",
"eslint": "^7",
"eslint-config-prettier": "^6",
"ts-node": "^9",
"typescript": "^4"
}
}
239 changes: 171 additions & 68 deletions src/commands/add.ts
Original file line number Diff line number Diff line change
@@ -1,83 +1,50 @@
import type { Fbi } from '../fbi'
import type { Factory } from '../core/factory'

import { join } from 'path'
import { join, relative } from 'path'
import { Command } from '../core/command'
import { git, isGitUrl, formatName } from '../utils'
import { git, isGitUrl, pathResolve, remotePkgVersion } from '../utils'

export default class CommandAdd extends Command {
id = 'add'
alias = ''
args = '<repositories...>'
description = `add factories from remote git repositories`
flags = [['-p, --package-manager <name>', 'Specifying a package manager. e.g. pnpm/yarn/npm']]
args = '<factories...>'
description = `add factories from npm module or git url`
flags = [
['-l, --local', 'Add to local node_modules', false],
['-p, --package-manager <name>', 'Specifying a package manager. e.g. pnpm/yarn/npm']
]

constructor(public factory: Fbi) {
super()
}

public async run(repositories: any, flags: any): Promise<Factory[]> {
public async run(names: any, flags: any): Promise<Factory[]> {
this.debug(`Running command "${this.id}" from factory "${this.factory.id}" with options:`, {
repositories,
names,
flags
})
const config = this.context.get('config')
const rootDir = join(config?.rootDirectory, config?.directoryName)
const result: Factory[] = []

for (const repo of repositories) {
const info = this.getBaseInfo(repo, config)
if (!info) {
continue
}
const targetDir = join(rootDir, info.name)
// 添加的仓库是否已存在
const exist = await this.fs.pathExists(targetDir)
if (exist) {
// 若已存在则更新模板
await this.update(targetDir, info.name)
} else {
const valid = await this.checkGitUrl(info.url)
if (!valid) {
continue
}
this.debug(`git clone ${info.url} ${targetDir}`)
await this.add(info.name, info.url, targetDir)
}

await this.install(flags || {}, targetDir)
const factory = this.factory.createFactory(targetDir)
for (const name of names) {
// 1. try npm module
let factory = await this.addFromNpm(name)
console.log({ factory })

if (!factory) {
this.error(`Factory '${info.name}' create field`)
continue
}

// save to store
const data = {
id: factory.id,
type: 'git',
from: info.url,
path: targetDir,
updatedAt: Date.now()
// 2. try git repository
factory = await this.addFromGit(name, flags)
}
this.debug('Save to store:', data)
this.store.set(data.id, data)

const { version, global } = await this.getVersionInfo(factory)
if (version) {
this.store.set(`${factory.id}.version`, version)
if (factory) {
result.push(factory)
}
if (global) {
this.store.set(`${factory.id}.global`, global)
}
result.push(factory)
}

return result
}

private getBaseInfo(url: string, { organization }: any) {
private getGitUrlInfo(url: string, { organization }: any) {
let gitUrl = ''
if (isGitUrl(url)) {
gitUrl = url
Expand Down Expand Up @@ -116,29 +83,165 @@ export default class CommandAdd extends Command {
return true
}

private async add(name: string, src: string, dest: string) {
const _name = this.style.cyan(name)
const spinner = this.createSpinner(`Adding ${_name} from '${src}'`).start()
private async addFromNpm(name: string, cwd = process.cwd()): Promise<null | Factory> {
const factoryExist = await this.factoryExist(name, 'npm', cwd)
this.debug('factoryExist:', factoryExist)

if (!factoryExist) {
const remotePkgExist = await remotePkgVersion(name)
if (!remotePkgExist) {
return null
}

const relativePath = relative(process.cwd(), cwd)

const anwser = (await this.prompt({
type: 'confirm',
name: 'confirm',
message: `'${name}' will be installed in the ${
relativePath || 'current'
} directory, continue?`,
initial: true
})) as any

if (!anwser.confirm) {
this.exit()
return null
}
}

const styledName = this.style.cyan(name)
const spinner = this.createSpinner(
`${factoryExist ? 'Updating' : 'Installing'} ${styledName}`
).start()

try {
await git.clone(`${src} ${dest}`, {
stdout: 'inherit'
await this.exec.command(`npm install --no-package-lock ${name}`, {
cwd
})
spinner.succeed(`Added ${_name}`)
spinner.succeed(`${factoryExist ? 'Updated' : 'Installed'} ${styledName}`)
return this.factory.resolveFromLocal(name, cwd)
} catch (err) {
spinner.fail(`Failed to add ${name}`)
spinner.fail(`Failed to ${factoryExist ? 'update' : 'install'} ${name}`)
this.error(err)
}
return null
}

private async update(targetDir: string, name: string) {
const spinner = this.createSpinner(`Already exist: ${name}. Try updating...`).start()
await git.hardReset('', {
cwd: targetDir
})
await git.pull('', {
cwd: targetDir
})
spinner.succeed(`Updated`)
private async addFromGit(name: string, flags: any): Promise<null | Factory> {
let targetDir
let isUpdate
let remoteUrl
let factoryName = name
const config = this.context.get('config')

let found = await this.factoryExist(name, 'git')
if (found) {
targetDir = found
factoryName = name
isUpdate = true
} else {
const info = this.getGitUrlInfo(name, config)
if (!info) {
return null
}

remoteUrl = info.url
factoryName = info.name

if (factoryName) {
let found2 = await this.factoryExist(factoryName, 'git')
if (found2) {
targetDir = found2
isUpdate = true
}
}
}

targetDir = targetDir || join(config?.rootDirectory, config?.directoryName, factoryName)
remoteUrl =
remoteUrl ||
(await git.remoteUrl({
cwd: targetDir
}))

this.debug({ remoteUrl, targetDir, isUpdate, factoryName })

const styledName = this.style.cyan(factoryName)
const spinner = this.createSpinner(`${isUpdate ? 'Updating' : 'Adding'} ${styledName}`).start()

if (!remoteUrl) {
spinner.fail(`Can not resolve git url`)
return null
}

try {
if (isUpdate) {
await git.hardReset('', {
cwd: targetDir
})
await git.pull('', {
cwd: targetDir
})
} else {
const urlValid = await this.checkGitUrl(remoteUrl)
if (!urlValid) {
spinner.fail(`Failed to add ${factoryName}`)
return null
}

await git.clone(`${remoteUrl} ${targetDir}`, {
stdout: 'inherit'
})
}

spinner.succeed(`${isUpdate ? 'Updated' : 'Added'} ${styledName}`)
await this.install(flags || {}, targetDir)

const factory = this.factory.createFactory(targetDir)
if (!factory) {
return null
}

// save to store
const data = {
id: factory.id,
type: 'git',
from: remoteUrl,
path: targetDir,
updatedAt: Date.now()
}
this.debug('Save to store:', data)
this.store.set(data.id, data)

const { version, global } = await this.getVersionInfo(factory)
if (version) {
this.store.set(`${factory.id}.version`, version)
}
if (global) {
this.store.set(`${factory.id}.global`, global)
}

return factory
} catch (err) {
spinner.fail(`Failed to ${isUpdate ? 'update' : 'add'} ${name}`)
this.error(err)
}

return null
}

private async factoryExist(name: string, type: 'npm' | 'git', cwd = process.cwd()) {
if (type === 'npm') {
return pathResolve(name, {
paths: [cwd]
})
} else {
const config = this.context.get('config')
const targetDir = join(config?.rootDirectory, config?.directoryName, name)
const exist = await this.fs.pathExists(targetDir)
return exist ? targetDir : ''
}
}

private async install(flags: Record<string, any>, targetDir: string) {
Expand Down
2 changes: 2 additions & 0 deletions src/commands/remove.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export default class CommandRemove extends Command {
`Removing ${this.style.yellow.bold(factory.id)} from the store...`
).start()

// TODO: remove versions dir

await this.deleteConfig(factory)

if (factory.type !== 'local') {
Expand Down
22 changes: 7 additions & 15 deletions src/fbi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,20 +106,20 @@ export class Fbi extends Factory {
// create from local
const local = this.context.get('config.factory')
if (local?.id && !this.factories.find((f) => f.id === local.id)) {
this.resolveFromLocal(local.id, local.version)
this.resolveFromLocal(local.id)
}

return this.factories
}

public resolveFactory(targetId: string, targetVersion?: string) {
public resolveFactory(targetId: string, targetVersion?: string, cwd = process.cwd()) {
this.debug('Fbi<resolveFactory>:', targetId, targetVersion)
let found = this.resolveFromCache(targetId, targetVersion)
if (found) {
return found
}

found = this.resolveFromLocal(targetId, targetVersion)
found = this.resolveFromLocal(targetId, cwd)
if (found) {
return found
}
Expand Down Expand Up @@ -162,7 +162,7 @@ export class Fbi extends Factory {
return targetId ? allTemplates.filter((t: Template) => t.id === targetId) : allTemplates
}

private resolveFromCache(targetId: string, targetVersion?: string) {
public resolveFromCache(targetId: string, targetVersion?: string) {
const factory = this.factories.find((f) => f?.id === targetId)
if (!factory) {
return null
Expand All @@ -171,7 +171,7 @@ export class Fbi extends Factory {
return factory
}

private resolveFromGlobal(targetId: string, targetVersion?: string) {
public resolveFromGlobal(targetId: string, targetVersion?: string) {
// from store
const factoryInfo: FactoryInfo = this.store.get(targetId)
if (!factoryInfo) {
Expand All @@ -194,23 +194,15 @@ export class Fbi extends Factory {
return this.createFactory(factoryInfo.path)
}

private resolveFromLocal(targetId: string, targetVersion?: string) {
public resolveFromLocal(targetId: string, cwd = process.cwd()) {
// from node_modules
const filePath = pathResolve(targetId, {
paths: [process.cwd()]
paths: [cwd]
})
if (!filePath) {
return null
}

// const idx = filePath.lastIndexOf('node_modules')
// console.log({ filePath, idx })
// if (idx === -1) {
// this.error(`Cann't resolve local factory which is not in node_modules`)
// return null
// }
// const dir = filePath.slice(0, idx) + 'node_modules/' + targetId

this.debug(`Factory "${targetId}" found in local`)
return this.createFactory(filePath)
}
Expand Down
Loading

0 comments on commit 5fc08d0

Please sign in to comment.