Skip to content
This repository has been archived by the owner on Aug 22, 2023. It is now read-only.

Commit

Permalink
Re: Fix plugins not being found when no node_modules exists (#289)
Browse files Browse the repository at this point in the history
* Implement test for plugin load

This test should pass when it has been updated to use resolve

* Update findRoot to use resolve function

* Fix findRoot not working due to incorrect assumptions

Assume resolve returns an entry file not a directory

* use `require.resolve` instead of `resolve` package

* remove `resolve` types

* return directory instead of resolved file

* undo previous commit

* adds root to one of the starting resolution paths

* fallback to legacy dependency resolution if not found

* grammar

Co-authored-by: Ryan Paroz <[email protected]>
  • Loading branch information
benatshippabo and TPHRyan authored Dec 6, 2021
1 parent 47a4711 commit 208b2d7
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 12 deletions.
47 changes: 35 additions & 12 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {Manifest} from './manifest'
import {PJSON} from './pjson'
import {Topic} from './topic'
import {tsPath} from './ts-node'
import {compact, exists, flatMap, loadJSON, mapValues} from './util'
import {compact, exists, resolvePackage, flatMap, loadJSON, mapValues} from './util'

const ROOT_INDEX_CMD_ID = ''

Expand Down Expand Up @@ -98,23 +98,35 @@ function topicsToArray(input: any, base?: string): Topic[] {
})
}

// eslint-disable-next-line valid-jsdoc
// essentially just "cd .."
function * up(from: string) {
while (path.dirname(from) !== from) {
yield from
from = path.dirname(from)
}
yield from
}

async function findSourcesRoot(root: string) {
for (const next of up(root)) {
const cur = path.join(next, 'package.json')
// eslint-disable-next-line no-await-in-loop
if (await exists(cur)) return path.dirname(cur)
}
}

/**
* @returns string
* @param name string
* @param root string
* find package root
* for packages installed into node_modules this will go up directories until
* it finds a node_modules directory with the plugin installed into it
*
* This is needed because of the deduping npm does
* This is needed because some oclif plugins do not declare the `main` field in their package.json
* https://github.com/oclif/config/pull/289#issuecomment-983904051
*/
async function findRoot(name: string | undefined, root: string) {
// essentially just "cd .."
function * up(from: string) {
while (path.dirname(from) !== from) {
yield from
from = path.dirname(from)
}
yield from
}
async function findRootLegacy(name: string | undefined, root: string): Promise<string | undefined> {
for (const next of up(root)) {
let cur
if (name) {
Expand All @@ -134,6 +146,17 @@ async function findRoot(name: string | undefined, root: string) {
}
}

async function findRoot(name: string | undefined, root: string) {
if (name) {
let pkgPath
try {
pkgPath = resolvePackage(name, {paths: [__dirname, root]})
} catch (error) {}
return pkgPath ? findSourcesRoot(path.dirname(pkgPath)) : findRootLegacy(name, root)
}
return findSourcesRoot(root)
}

export class Plugin implements IPlugin {
// static loadedPlugins: {[name: string]: Plugin} = {}
_base = `${_pjson.name}@${_pjson.version}`
Expand Down
4 changes: 4 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ export function exists(path: string): Promise<boolean> {
return new Promise(resolve => resolve(fs.existsSync(path)))
}

export function resolvePackage(id: string, paths: { paths: string[] }): string {
return require.resolve(id, paths)
}

export function loadJSON(path: string): Promise<any> {
debug('loadJSON %s', path)
// let loadJSON
Expand Down
53 changes: 53 additions & 0 deletions test/plugin.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import * as path from 'path'

import {expect, fancy} from './test'
import * as Plugin from '../src/plugin'
import * as util from '../src/util'

const root = path.resolve(__dirname, 'fixtures/typescript')
const pluginName = '@oclif/plugin-help'
const pluginLocation = 'some/external/directory'
const pluginPjsonLocation = path.join(pluginLocation, 'package.json')

const withPluginInstance = () => {
return fancy
.add('plugin', () => new Plugin.Plugin({
type: 'core',
root,
name: pluginName,
ignoreManifest: true,
}))
.stub(util, 'exists', (checkPath: string) => checkPath === pluginPjsonLocation)
.stub(util, 'resolvePackage', (id: string): string => {
if (id !== pluginName) {
throw new Error(`resolvePackage: expected ${pluginName} but got ${id}`)
}
return path.join(pluginLocation, 'lib', 'index.js')
})
.stub(util, 'loadJSON', (jsonPath: string) => {
if (jsonPath !== pluginPjsonLocation) {
return {}
}
return {
name: pluginName,
version: '1.0.0',
files: [],
oclif: {},
}
})
}

describe('Plugin', () => {
withPluginInstance()
.it('Should correctly instantiate a Plugin instance', ctx => {
expect(ctx.plugin).to.be.instanceOf(
Plugin.Plugin,
'Expected instance to be an instance of Plugin!',
)
})

withPluginInstance()
.it('Should correctly load a Plugin', async ctx => {
await ctx.plugin.load()
})
})

0 comments on commit 208b2d7

Please sign in to comment.