Skip to content

Commit

Permalink
fix(core): fix require.context shim for graphql deploys (#2903)
Browse files Browse the repository at this point in the history
  • Loading branch information
ricokahler committed Nov 1, 2021
1 parent f298d48 commit 808c459
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 143 deletions.
2 changes: 2 additions & 0 deletions packages/@sanity/core/_internal.js
@@ -0,0 +1,2 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('./_interopReexport')(module.exports, require('./lib/_exports/_internal'))
30 changes: 30 additions & 0 deletions packages/@sanity/core/_interopReexport.js
@@ -0,0 +1,30 @@
/**
* esm/commonjs interop reexport helper
* this should be used instead of doing `module.exports = require('./some/file')
* since that doesn't work properly with esm imports from the consuming end
* @param moduleExports export object from the exporting module
* @param importedModule the imported module to be reexported
*/
module.exports = function _interopReexport(moduleExports, importedModule) {
Object.defineProperty(moduleExports, '__esModule', {
value: true,
})

Object.defineProperty(moduleExports, 'default', {
enumerable: true,
get: function get() {
return importedModule.default
},
})

Object.keys(importedModule).forEach(function (key) {
if (key === 'default' || key === '__esModule') return
if (key in moduleExports && moduleExports[key] === importedModule[key]) return
Object.defineProperty(moduleExports, key, {
enumerable: true,
get: function get() {
return importedModule[key]
},
})
})
}
9 changes: 9 additions & 0 deletions packages/@sanity/core/package.json
Expand Up @@ -5,6 +5,14 @@
"main": "lib/index.js",
"author": "Sanity.io <hello@sanity.io>",
"license": "MIT",
"//": "the typesVersion config below is a workaround for TypeScript's lack of support for package exports",
"typesVersions": {
"*": {
"*": [
"dist/dts/_exports/*"
]
}
},
"scripts": {
"build": "babel src --copy-files --out-dir lib",
"clean": "rimraf lib dest",
Expand Down Expand Up @@ -74,6 +82,7 @@
"@sanity/client": "2.21.7",
"@types/fs-extra": "^7.0.0",
"@types/inquirer": "^6.0.2",
"@types/klaw-sync": "^6.0.1",
"@types/resolve-from": "^4.0.0",
"@types/semver": "^6.2.3",
"chalk": "^2.4.2",
Expand Down
1 change: 1 addition & 0 deletions packages/@sanity/core/src/_exports/_internal.ts
@@ -0,0 +1 @@
export {createRequireContext} from '../util/createRequireContext'
2 changes: 2 additions & 0 deletions packages/@sanity/core/src/_exports/index.ts
@@ -0,0 +1,2 @@
// intentionally left blank for now since this package have never had any official package exports before
export {}
98 changes: 98 additions & 0 deletions packages/@sanity/core/src/util/createRequireContext.ts
@@ -0,0 +1,98 @@
import path from 'path'
import klaw from 'klaw-sync'

/**
* https://webpack.js.org/guides/dependency-management/#requirecontext
*/
interface WebpackRequireContextFactory {
(
directory: string,
useSubdirectories?: boolean,
regExp?: RegExp,
mode?: 'sync'
): WebpackRequireContext
}

/**
* https://webpack.js.org/guides/dependency-management/#context-module-api
*/
interface WebpackRequireContext {
(request: string): unknown

id: string
keys(): string[]
resolve(request: string): unknown
}

/**
* note, options are primarily for testing
*/
interface CreateRequireContextOptions {
require?: (request: string) => unknown
resolve?: (request: string) => string
}

const globalRequire = require

export function createRequireContext(
dirname: string,
{
require = globalRequire,
resolve = globalRequire.resolve.bind(globalRequire),
}: CreateRequireContextOptions = {}
): WebpackRequireContextFactory {
function requireContext(
directory: string,
recursive = true,
regExp = /.*/
): WebpackRequireContext {
console.warn('Usage of `require.context` is deprecated and will break in a future release.')

try {
const basedir = path.resolve(dirname, directory)

const keys = klaw(basedir, {
depthLimit: recursive ? 30 : 0,
})
.filter((item) => !item.stats.isDirectory() && regExp.test(item.path))
// use relative paths for the keys
.map((item) => path.relative(basedir, item.path))
// if the path.resolve doesn't prefixed with `./` then add it.
// note it could be upward `../` so we need the conditional
.map((filename) => (filename.startsWith('.') ? filename : `./${filename}`))

// eslint-disable-next-line no-inner-declarations
function context(request: string) {
// eslint-disable-next-line import/no-dynamic-require
return require(path.join(basedir, request))
}

Object.defineProperty(context, 'id', {
get: () => {
console.warn('`require.context` `context.id` is not supported.')
return ''
},
})

Object.defineProperty(context, 'keys', {
// NOTE: this keys method does not match the behavior of webpack's
// require.context context because it does not return all possible keys
//
// e.g. `./module-a/index.js`
// would return `['./module-a', './module-a/index', './module-a/index.js']`
value: () => keys,
})

Object.defineProperty(context, 'resolve', {
value: (request: string) => resolve(path.join(basedir, request)),
})

return context as WebpackRequireContext
} catch (contextErr) {
contextErr.message = `Error running require.context():\n\n${contextErr.message}`
throw contextErr
}
}

return requireContext
}
4 changes: 2 additions & 2 deletions packages/@sanity/core/src/util/mockBrowserEnvironment.js
Expand Up @@ -38,7 +38,6 @@ function mockBrowserEnvironment(basePath) {
const domCleanup = jsdomGlobal(jsdomDefaultHtml, {url: 'http://localhost:3333/'})
const windowCleanup = () => global.window.close()
const globalCleanup = provideFakeGlobals()
const contextCleanup = requireContext.register()
const cleanupFileLoader = pirates.addHook(
(code, filename) => `module.exports = ${JSON.stringify(filename)}`,
{
Expand All @@ -49,13 +48,14 @@ function mockBrowserEnvironment(basePath) {

registerBabelLoader(basePath)
pluginLoader({basePath, stubCss: true})
const contextCleanup = requireContext.register()

return function cleanupBrowserEnvironment() {
cleanupFileLoader()
contextCleanup()
globalCleanup()
windowCleanup()
domCleanup()
contextCleanup()
}
}

Expand Down
141 changes: 0 additions & 141 deletions packages/@sanity/core/src/util/requireContext.js

This file was deleted.

34 changes: 34 additions & 0 deletions packages/@sanity/core/src/util/requireContext.ts
@@ -0,0 +1,34 @@
/**
* This file exposes a "register" function which hacks in a `require.context`function,
* mirroring webpacks version (https://webpack.js.org/guides/dependency-management/#requirecontext)
*
* Basically allows you to do `require.context('./types', true, /\.js$/)` to import multiple
* files at the same time. While generally not advised (given it's a webpack-only thing),
* people are already using it in the wild, so it breaks when trying to deploy GraphQL APIs,
* or when running scripts using `sanity exec`.
*
* We have to inject the `require.context` function to each required file, which is done by
* overriding injecting a small script that runs before the start of each file.
*/

import path from 'path'
import {addHook} from 'pirates'

type Revert = ReturnType<typeof addHook>
let revert: Revert | undefined

const augmentRequire = (dirname: string) =>
`if (typeof require !== 'undefined') {const {createRequireContext} = require('@sanity/core/_internal');require.context = createRequireContext(${JSON.stringify(
dirname
)});};`

export function register(): Revert {
if (revert) return revert

revert = addHook((code, filename) => `${augmentRequire(path.dirname(filename))}${code}`, {
exts: ['.js', '.ts', '.tsx'],
ignoreNodeModules: true,
})

return revert
}

3 comments on commit 808c459

@vercel
Copy link

@vercel vercel bot commented on 808c459 Nov 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

studio-workshop – ./dev/workshop

studio-workshop.sanity.build
studio-workshop-git-next.sanity.build

@vercel
Copy link

@vercel vercel bot commented on 808c459 Nov 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

test-studio – ./

test-studio-git-next.sanity.build
test-studio.sanity.build

@vercel
Copy link

@vercel vercel bot commented on 808c459 Nov 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

perf-studio – ./

perf-studio.sanity.build
perf-studio-git-next.sanity.build

Please sign in to comment.