Skip to content

Commit

Permalink
feat: adding support for child lookups
Browse files Browse the repository at this point in the history
  • Loading branch information
martinheidegger committed Feb 22, 2022
1 parent 8a03c5e commit 5826950
Show file tree
Hide file tree
Showing 9 changed files with 1,090 additions and 44 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ cjs
mjs
node_modules
*.tgz
coverage
69 changes: 30 additions & 39 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { spawn } from 'child_process'
import { mkdir, readFile, writeFile, stat, utimes } from 'fs/promises'
import dbg from 'debug'
import * as path from 'path'
import { getMatcher, normalize } from './util'

const debug = dbg('@tradle/lambda-plugins')

Expand Down Expand Up @@ -199,47 +200,24 @@ async function assertInstalled (plugins: FNOrResult<string[]>, { tmpDir, maxAge
}
}

type Cause = {
useImport: boolean,
cause: string
}

const CAUSE_FALLBACK: Cause = { cause: 'require', useImport: false }
const CAUSE_TYPE: Cause = { cause: 'import because of type=module', useImport: true }
const CAUSE_MODULE: Cause = { cause: 'import because of a defined module', useImport: true }
const CAUSE_DOT_EXPORT: Cause = { cause: 'import because of exports["."]', useImport: true }
const CAUSE_DOT_ANY: Cause = { cause: 'import because of exports["./*"]', useImport: true }

function fuzzyChooseImport (pkg: any): Cause {
if (pkg.type === 'module') return CAUSE_TYPE
if (pkg.module !== undefined) return CAUSE_MODULE
if (pkg.exports) {
if (pkg.exports['.']?.import !== undefined)
return CAUSE_DOT_EXPORT
if (pkg.exports['./*']?.import !== undefined)
return CAUSE_DOT_ANY
}
return CAUSE_FALLBACK
}

async function loadData (name: string, depPath: string, pkg: any): Promise<any> {
const { cause, useImport } = fuzzyChooseImport(pkg)
debug('Loading package for %s from %s (%s)', name, depPath, cause)
return useImport ? await import(depPath) : require(depPath)
}

async function loadPackage (name: string, depPath: string): Promise<any> {
const pkgPath = path.join(depPath, 'package.json')
debug('Loading package.json for %s from %s', name, pkgPath)
const data = await readFile(pkgPath, 'utf-8')
return JSON.parse(data)
let raw = '{}'
try {
raw = await readFile(pkgPath, 'utf-8')
debug('Using package.json for %s from %s', name, pkgPath)
} catch (err) {
// Package json is optional
debug('No package.json found at %s, using regular lookup', pkgPath)
}
return JSON.parse(raw)
}

export class Plugin {
readonly name: string
readonly path: string

#data: Promise<any> | undefined
#data: { [child: string]: Promise<any> } | undefined
#pkg: Promise<any> | undefined

constructor (name: string, path: string) {
Expand All @@ -256,13 +234,26 @@ export class Plugin {
return pkg
}

data (opts?: { force?: boolean }): Promise<any> {
let data = this.#data
if (data === undefined || opts?.force) {
data = this.package(opts).then(pkg => loadData(this.name, this.path, pkg))
this.#data = data
async #loadData (child: string, force: boolean) {
const pkg = await this.package({ force })
const matcher = getMatcher(this.path, pkg)
const mjs = matcher(child, 'module')
if (mjs !== undefined && mjs.location !== null) {
debug('Importing package for %s from %s (%s)', this.name, mjs.location, mjs.cause)
return await import(mjs.location)
}
return data
const cjs = matcher(child, 'commonjs')
if (cjs !== undefined && cjs.location !== null) {
debug('Requiring package for %s from %s (%s)', this.name, cjs.location, cjs.cause)
return require(cjs.location)
}
throw new Error(`Can not require or import a package for ${this.name} at ${this.path}`)
}

data (opts?: { force?: boolean, child?: string }): Promise<any> {
const all = this.#data ?? (this.#data = {})
const child = normalize(opts?.child)
return all[child] ?? (all[child] = this.#loadData(child, opts?.force ?? false))
}
}

Expand Down
Loading

0 comments on commit 5826950

Please sign in to comment.