Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/gatsby/src/bootstrap/__mocks__/require/exports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
exports.foo = () => {}
exports.bar = () => {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Object.defineProperty(exports, `foo`, {
enumerable: true,
get: function get() {
return () => {}
},
})
42 changes: 30 additions & 12 deletions packages/gatsby/src/bootstrap/__tests__/resolve-module-exports.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ describe(`Resolve module exports`, () => {
})

it(`Show meaningful error message for invalid JavaScript`, () => {
resolveModuleExports(`/bad/file`, resolver)
resolveModuleExports(`/bad/file`, { resolver })
expect(
reporter.panic.mock.calls.map(c =>
// Remove console colors + trim whitespace
Expand All @@ -145,57 +145,75 @@ describe(`Resolve module exports`, () => {
})

it(`Resolves an export`, () => {
const result = resolveModuleExports(`/simple/export`, resolver)
const result = resolveModuleExports(`/simple/export`, { resolver })
expect(result).toEqual([`foo`])
})

it(`Resolves multiple exports`, () => {
const result = resolveModuleExports(`/multiple/export`, resolver)
const result = resolveModuleExports(`/multiple/export`, { resolver })
expect(result).toEqual([`bar`, `baz`, `foo`])
})

it(`Resolves an export from an ES6 file`, () => {
const result = resolveModuleExports(`/import/with/export`, resolver)
const result = resolveModuleExports(`/import/with/export`, { resolver })
expect(result).toEqual([`baz`])
})

it(`Resolves an exported const`, () => {
const result = resolveModuleExports(`/export/const`, resolver)
const result = resolveModuleExports(`/export/const`, { resolver })
expect(result).toEqual([`fooConst`])
})

it(`Resolves module.exports`, () => {
const result = resolveModuleExports(`/module/exports`, resolver)
const result = resolveModuleExports(`/module/exports`, { resolver })
expect(result).toEqual([`barExports`])
})

it(`Resolves exports from a larger file`, () => {
const result = resolveModuleExports(`/realistic/export`, resolver)
const result = resolveModuleExports(`/realistic/export`, { resolver })
expect(result).toEqual([`replaceHistory`, `replaceComponentRenderer`])
})

it(`Ignores exports.__esModule`, () => {
const result = resolveModuleExports(`/esmodule/export`, resolver)
const result = resolveModuleExports(`/esmodule/export`, { resolver })
expect(result).toEqual([`foo`])
})

it(`Resolves a named export`, () => {
const result = resolveModuleExports(`/export/named`, resolver)
const result = resolveModuleExports(`/export/named`, { resolver })
expect(result).toEqual([`foo`])
})

it(`Resolves a named export from`, () => {
const result = resolveModuleExports(`/export/named/from`, resolver)
const result = resolveModuleExports(`/export/named/from`, { resolver })
expect(result).toEqual([`Component`])
})

it(`Resolves a named export as`, () => {
const result = resolveModuleExports(`/export/named/as`, resolver)
const result = resolveModuleExports(`/export/named/as`, { resolver })
expect(result).toEqual([`bar`])
})

it(`Resolves multiple named exports`, () => {
const result = resolveModuleExports(`/export/named/multiple`, resolver)
const result = resolveModuleExports(`/export/named/multiple`, { resolver })
expect(result).toEqual([`foo`, `bar`, `baz`])
})

it(`Resolves exports when using require mode - simple case`, () => {
jest.mock(`require/exports`)

const result = resolveModuleExports(`require/exports`, {
mode: `require`,
})
expect(result).toEqual([`foo`, `bar`])
})

it(`Resolves exports when using require mode - unusual case`, () => {
jest.mock(`require/unusual-exports`)

const result = resolveModuleExports(`require/unusual-exports`, {
mode: `require`,
})
expect(result).toEqual([`foo`])
})
})
5 changes: 4 additions & 1 deletion packages/gatsby/src/bootstrap/load-plugins/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,10 @@ const collatePluginAPIs = ({ apis, flattenedPlugins }) => {
// the plugin node itself *and* in an API to plugins map for faster lookups
// later.
const pluginNodeExports = resolveModuleExports(
`${plugin.resolve}/gatsby-node`
`${plugin.resolve}/gatsby-node`,
{
mode: `require`,
}
)
const pluginBrowserExports = resolveModuleExports(
`${plugin.resolve}/gatsby-browser`
Expand Down
33 changes: 23 additions & 10 deletions packages/gatsby/src/bootstrap/resolve-module-exports.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,7 @@ const { codeFrameColumns } = require(`@babel/code-frame`)
const { babelParseToAst } = require(`../utils/babel-parse-to-ast`)
const report = require(`gatsby-cli/lib/reporter`)

/**
* Given a `require.resolve()` compatible path pointing to a JS module,
* return an array listing the names of the module's exports.
*
* Returns [] for invalid paths and modules without exports.
*
* @param {string} modulePath
* @param {function} resolver
*/
module.exports = (modulePath, resolver = require.resolve) => {
const staticallyAnalyzeExports = (modulePath, resolver = require.resolve) => {
let absPath
const exportNames = []

Expand Down Expand Up @@ -118,3 +109,25 @@ https://gatsby.dev/no-mixed-modules
}
return exportNames
}

/**
* Given a `require.resolve()` compatible path pointing to a JS module,
* return an array listing the names of the module's exports.
*
* Returns [] for invalid paths and modules without exports.
*
* @param {string} modulePath
* @param {string} mode
* @param {function} resolver
*/
module.exports = (modulePath, { mode = `analisys`, resolver } = {}) => {
if (mode === `require`) {
try {
return Object.keys(require(modulePath))
} catch {
return []
}
} else {
return staticallyAnalyzeExports(modulePath, resolver)
}
}