Comparing version 0.6.17 to 0.7.0
@@ -14,2 +14,22 @@ { | ||
}, | ||
"..": { | ||
"version": "0.6.17", | ||
"dev": true, | ||
"license": "MIT", | ||
"dependencies": { | ||
"lodash": "^4.17.21", | ||
"resolve": "^1.22.1" | ||
}, | ||
"devDependencies": { | ||
"core-assert": "^1.0.0", | ||
"is-number": "^7.0.0", | ||
"is-promise": "^4.0.0", | ||
"standard": "^17.0.0", | ||
"teenytest": "^6.0.4", | ||
"teenytest-promise": "^1.0.0" | ||
}, | ||
"engines": { | ||
"node": ">= 0.14.0" | ||
} | ||
}, | ||
"node_modules/ansi-colors": { | ||
@@ -387,8 +407,2 @@ "version": "4.1.1", | ||
}, | ||
"node_modules/function-bind": { | ||
"version": "1.1.1", | ||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", | ||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", | ||
"dev": true | ||
}, | ||
"node_modules/get-caller-file": { | ||
@@ -466,14 +480,2 @@ "version": "2.0.5", | ||
}, | ||
"node_modules/has": { | ||
"version": "1.0.3", | ||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", | ||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", | ||
"dev": true, | ||
"dependencies": { | ||
"function-bind": "^1.1.1" | ||
}, | ||
"engines": { | ||
"node": ">= 0.4.0" | ||
} | ||
}, | ||
"node_modules/has-flag": { | ||
@@ -525,14 +527,2 @@ "version": "4.0.0", | ||
}, | ||
"node_modules/is-core-module": { | ||
"version": "2.11.0", | ||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", | ||
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", | ||
"dev": true, | ||
"dependencies": { | ||
"has": "^1.0.3" | ||
}, | ||
"funding": { | ||
"url": "https://github.com/sponsors/ljharb" | ||
} | ||
}, | ||
"node_modules/is-extglob": { | ||
@@ -625,8 +615,2 @@ "version": "2.1.1", | ||
}, | ||
"node_modules/lodash": { | ||
"version": "4.17.21", | ||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", | ||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", | ||
"dev": true | ||
}, | ||
"node_modules/log-symbols": { | ||
@@ -793,8 +777,2 @@ "version": "4.1.0", | ||
}, | ||
"node_modules/path-parse": { | ||
"version": "1.0.7", | ||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", | ||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", | ||
"dev": true | ||
}, | ||
"node_modules/pathval": { | ||
@@ -822,13 +800,4 @@ "version": "1.1.1", | ||
"node_modules/quibble": { | ||
"version": "0.6.15", | ||
"resolved": "file:..", | ||
"dev": true, | ||
"license": "MIT", | ||
"dependencies": { | ||
"lodash": "^4.17.21", | ||
"resolve": "^1.20.0" | ||
}, | ||
"engines": { | ||
"node": ">= 0.14.0" | ||
} | ||
"resolved": "..", | ||
"link": true | ||
}, | ||
@@ -865,19 +834,2 @@ "node_modules/randombytes": { | ||
}, | ||
"node_modules/resolve": { | ||
"version": "1.22.1", | ||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", | ||
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", | ||
"dev": true, | ||
"dependencies": { | ||
"is-core-module": "^2.9.0", | ||
"path-parse": "^1.0.7", | ||
"supports-preserve-symlinks-flag": "^1.0.0" | ||
}, | ||
"bin": { | ||
"resolve": "bin/resolve" | ||
}, | ||
"funding": { | ||
"url": "https://github.com/sponsors/ljharb" | ||
} | ||
}, | ||
"node_modules/safe-buffer": { | ||
@@ -965,14 +917,2 @@ "version": "5.2.1", | ||
}, | ||
"node_modules/supports-preserve-symlinks-flag": { | ||
"version": "1.0.0", | ||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", | ||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", | ||
"dev": true, | ||
"engines": { | ||
"node": ">= 0.4" | ||
}, | ||
"funding": { | ||
"url": "https://github.com/sponsors/ljharb" | ||
} | ||
}, | ||
"node_modules/to-regex-range": { | ||
@@ -979,0 +919,0 @@ "version": "5.0.1", |
const path = require('path') | ||
// These functions are in a separate file due to the fact that we need to support Node.js v8, which | ||
// cannot parse the `import` syntax. | ||
// The way it is dealt with is that we require this module only in code paths in `quibble.js` | ||
// that are ESM related. | ||
const { pathToFileURL } = require('url') | ||
exports.dummyImportModuleToGetAtPath = async function dummyImportModuleToGetAtPath (modulePath) { | ||
try { | ||
if (path.isAbsolute(modulePath)) { | ||
modulePath = 'file://' + modulePath | ||
} | ||
await import(modulePath + (modulePath.includes('?') ? '&' : '?') + '__quibbleresolvepath') | ||
const moduleUrl = path.isAbsolute(modulePath) ? pathToFileURL(modulePath) : modulePath | ||
await import(addQueryToUrl(moduleUrl, '__quibbleresolveurl')) | ||
} catch (error) { | ||
if (error.code === 'QUIBBLE_RESOLVED_PATH') { | ||
return error.resolvedPath | ||
if (error.code === 'QUIBBLE_RESOLVED_URL') { | ||
return error.resolvedUrl | ||
} else { | ||
@@ -27,6 +23,13 @@ throw error | ||
exports.importOriginalModule = async (fullImportPath) => { | ||
if (path.isAbsolute(fullImportPath)) { | ||
fullImportPath = 'file://' + fullImportPath | ||
return import(addQueryToUrl(fullImportPath, '__quibbleoriginal')) | ||
} | ||
function addQueryToUrl (url, query) { | ||
try { | ||
const urlObject = new URL(url) | ||
urlObject.searchParams.set(query, '') | ||
return urlObject.href.replace(`${query}=`, query) | ||
} catch { | ||
return url + '?' + query | ||
} | ||
return import(fullImportPath + '?__quibbleoriginal') | ||
} |
const Module = require('module') | ||
const path = require('path') | ||
const util = require('util') | ||
const { URL } = require('url') | ||
const { URL, pathToFileURL, fileURLToPath } = require('url') | ||
const resolve = require('resolve') | ||
const importFunctions = require('./esm-import-functions') | ||
const isPlainObject = require('lodash/isPlainObject.js') | ||
@@ -23,3 +24,2 @@ const _ = { | ||
} | ||
let importFunctionsModule | ||
@@ -32,2 +32,4 @@ const originalLoad = Module._load | ||
const quibbleUserToLoaderCommunication = () => globalThis[Symbol.for('__quibbleUserToLoaderCommunication')] | ||
module.exports = quibble = function (request, stub) { | ||
@@ -56,3 +58,3 @@ request = quibble.absolutify(request) | ||
} | ||
ignoredCallerFiles = _.uniq(ignoredCallerFiles.concat(file)) | ||
ignoredCallerFiles = _.uniq(ignoredCallerFiles.concat(file, pathToFileURL(file).href)) | ||
} | ||
@@ -64,5 +66,3 @@ | ||
if (global.__quibble) { | ||
delete global.__quibble.quibbledModules | ||
} | ||
quibbleUserToLoaderCommunication()?.reset() | ||
@@ -84,5 +84,4 @@ config = quibble.config() | ||
quibble.esm = async function (importPath, namedExportStubs, defaultExportStub) { | ||
quibble.esm = async function (specifier, namedExportStubs, defaultExportStub) { | ||
checkThatLoaderIsLoaded() | ||
if (namedExportStubs != null && !util.types.isProxy(namedExportStubs) && !isPlainObject(namedExportStubs)) { | ||
@@ -103,29 +102,11 @@ throw new Error('namedExportsStub argument must be either a plain object or null/undefined') | ||
if (!global.__quibble.quibbledModules) { | ||
global.__quibble.quibbledModules = new Map() | ||
} | ||
++global.__quibble.stubModuleGeneration | ||
importFunctionsModule = importFunctionsModule || require('./esm-import-functions') | ||
const importPathIsBareSpecifier = isBareSpecifier(specifier) | ||
const parentUrl = importPathIsBareSpecifier ? undefined : hackErrorStackToGetCallerFile(true, true) | ||
const moduleUrl = importPathIsBareSpecifier | ||
? await importFunctions.dummyImportModuleToGetAtPath(specifier) | ||
: new URL(specifier, parentUrl).href | ||
const importPathIsBareSpecifier = isBareSpecifier(importPath) | ||
const isAbsolutePath = path.isAbsolute(importPath) | ||
let callerFile | ||
if (!isAbsolutePath && !importPathIsBareSpecifier) { | ||
callerFile = hackErrorStackToGetCallerFile() | ||
if (process.platform === 'win32' && callerFile[0] === '/') { | ||
callerFile = callerFile.substring(1) | ||
} | ||
} | ||
const modulePath = importPathIsBareSpecifier | ||
? await importFunctionsModule.dummyImportModuleToGetAtPath(importPath) | ||
: isAbsolutePath | ||
? importPath | ||
: (path.resolve(path.dirname(callerFile), importPath)) | ||
const fullModulePath = process.platform !== 'win32' ? modulePath : (modulePath.match(/^[a-zA-Z]:/) ? '/' : '') + modulePath.split(path.sep).join('/') | ||
global.__quibble.quibbledModules.set(fullModulePath, { | ||
defaultExportStub, | ||
namedExportStubs: finalNamedExportStubs | ||
await quibbleUserToLoaderCommunication().addMockedModule(moduleUrl, { | ||
namedExportStubs: finalNamedExportStubs, | ||
defaultExportStub | ||
}) | ||
@@ -135,32 +116,21 @@ } | ||
quibble.isLoaderLoaded = function () { | ||
return !!global.__quibble | ||
return !!quibbleUserToLoaderCommunication() | ||
} | ||
quibble.esmImportWithPath = async function esmImportWithPath (importPath) { | ||
quibble.esmImportWithPath = async function esmImportWithPath (specifier) { | ||
checkThatLoaderIsLoaded() | ||
importFunctionsModule = importFunctionsModule || require('./esm-import-functions') | ||
const importPathIsBareSpecifier = isBareSpecifier(specifier) | ||
const parentUrl = importPathIsBareSpecifier ? undefined : hackErrorStackToGetCallerFile(true, true) | ||
const moduleUrl = importPathIsBareSpecifier | ||
? await importFunctions.dummyImportModuleToGetAtPath(specifier) | ||
: new URL(specifier, parentUrl).href | ||
const importPathIsBareSpecifier = isBareSpecifier(importPath) | ||
const isAbsolutePath = path.isAbsolute(importPath) | ||
let callerFile | ||
if (!isAbsolutePath && !importPathIsBareSpecifier) { | ||
callerFile = hackErrorStackToGetCallerFile() | ||
if (process.platform === 'win32' && callerFile[0] === '/') { | ||
callerFile = callerFile.substring(1) | ||
} | ||
} | ||
const modulePath = importPathIsBareSpecifier | ||
? await importFunctionsModule.dummyImportModuleToGetAtPath(importPath) | ||
: isAbsolutePath | ||
? importPath | ||
: (path.resolve(path.dirname(callerFile), importPath)) | ||
const fullImportPath = importPathIsBareSpecifier | ||
? importPath | ||
: (process.platform !== 'win32' ? modulePath : (modulePath.match(/^[a-zA-Z]:/) ? '/' : '') + modulePath.split(path.sep).join('/')) | ||
return { | ||
modulePath, | ||
module: await importFunctionsModule.importOriginalModule(fullImportPath) | ||
// The name of this property _should_ be `moduleUrl`, but it is used in `testdouble` as `modulePath` | ||
// and so can't be changed without breaking `testdouble`. So I add another field with the correct name | ||
// and once testdouble is updated, I can remove the `modulePath` field. | ||
modulePath: moduleUrl, | ||
moduleUrl, | ||
module: await importFunctions.importOriginalModule(moduleUrl) | ||
} | ||
@@ -238,6 +208,3 @@ } | ||
const hackErrorStackToGetCallerFile = function (includeGlobalIgnores) { | ||
if (includeGlobalIgnores == null) { | ||
includeGlobalIgnores = true | ||
} | ||
const hackErrorStackToGetCallerFile = function (includeGlobalIgnores = true, keepUrls = false) { | ||
const originalFunc = Error.prepareStackTrace | ||
@@ -250,12 +217,14 @@ const originalStackTraceLimit = Error.stackTraceLimit | ||
} | ||
const conversionFunc = keepUrls ? convertStackPathToUrl : convertStackUrlToPath | ||
const e = new Error() | ||
const currentFile = convertUrlToPath(e.stack[0].getFileName()) | ||
const currentFile = conversionFunc(e.stack[0].getFileName()) | ||
return _.flow([ | ||
_.invokeMap('getFileName'), | ||
_.compact, | ||
_.map(convertUrlToPath), | ||
_.map(conversionFunc), | ||
_.reject(function (f) { | ||
return includeGlobalIgnores && _.includes(f, ignoredCallerFiles) | ||
}), | ||
_.filter(path.isAbsolute), | ||
_.filter(keepUrls ? _.identity : path.isAbsolute), | ||
_.filter(conversionFunc), | ||
_.find(function (f) { | ||
@@ -272,3 +241,3 @@ return f !== currentFile | ||
function checkThatLoaderIsLoaded () { | ||
if (!global.__quibble) { | ||
if (!quibble.isLoaderLoaded()) { | ||
throw new Error('quibble loader not loaded. You cannot replace ES modules without a loader. Run node with `--loader=quibble`.') | ||
@@ -278,8 +247,7 @@ } | ||
function convertUrlToPath (fileUrl) { | ||
function convertStackUrlToPath (fileUrl) { | ||
try { | ||
const p = fileUrl.match(/^[a-zA-Z]:/) ? fileUrl : new URL(fileUrl).pathname | ||
return p | ||
return fileURLToPath(fileUrl) | ||
} catch (error) { | ||
if (error.code === 'ERR_INVALID_URL') { | ||
if (error.code !== 'TYPE_ERROR') { | ||
return fileUrl | ||
@@ -292,2 +260,10 @@ } else { | ||
function convertStackPathToUrl (filePath) { | ||
if (path.isAbsolute(filePath)) { | ||
return pathToFileURL(filePath).href | ||
} else { | ||
return filePath | ||
} | ||
} | ||
function isBareSpecifier (modulePath) { | ||
@@ -294,0 +270,0 @@ const firstLetter = modulePath[0] |
{ | ||
"name": "quibble", | ||
"version": "0.6.17", | ||
"version": "0.7.0", | ||
"description": "Makes it easy to replace require'd dependencies.", | ||
@@ -12,3 +12,3 @@ "homepage": "https://github.com/testdouble/quibble", | ||
}, | ||
"./": "./" | ||
"./package.json": "./package.json" | ||
}, | ||
@@ -15,0 +15,0 @@ "scripts": { |
const path = require('path') | ||
const { pathToFileURL } = require('url') | ||
const quibble = require('quibble') | ||
@@ -7,5 +8,6 @@ | ||
'support importing esm and returning the path for a relative url': async function () { | ||
const { modulePath, module } = await quibble.esmImportWithPath('../esm-fixtures/a-module.mjs') | ||
const { modulePath, moduleUrl, module } = await quibble.esmImportWithPath('../esm-fixtures/a-module.mjs') | ||
assert.deepEqual(modulePath, path.resolve(__dirname, '../esm-fixtures/a-module.mjs')) | ||
assert.deepEqual(modulePath, pathToFileURL(path.resolve(__dirname, '../esm-fixtures/a-module.mjs')).href) | ||
assert.deepEqual(moduleUrl, pathToFileURL(path.resolve(__dirname, '../esm-fixtures/a-module.mjs')).href) | ||
assert.deepEqual({ ...module }, { | ||
@@ -23,3 +25,3 @@ default: 'default-export', | ||
assert.deepEqual(modulePath, require.resolve('is-promise').replace('.js', '.mjs').replace(/\\/g, '/').replace(/^([a-zA-Z]:)/, '/$1')) | ||
assert.deepEqual(modulePath, pathToFileURL(require.resolve('is-promise')).href.replace('.js', '.mjs')) | ||
const { default: isPromise, ...rest } = module | ||
@@ -38,3 +40,3 @@ assert.deepEqual(rest, {}) | ||
assert.deepEqual(modulePath, path.resolve(__dirname, '../esm-fixtures/a-module.mjs')) | ||
assert.deepEqual(modulePath, pathToFileURL(path.resolve(__dirname, '../esm-fixtures/a-module.mjs')).href) | ||
assert.deepEqual({ ...module }, { | ||
@@ -53,3 +55,3 @@ default: 'default-export', | ||
assert.deepEqual(modulePath, require.resolve('is-promise').replace('.js', '.mjs').replace(/\\/g, '/').replace(/^([a-zA-Z]:)/, '/$1')) | ||
assert.deepEqual(modulePath, pathToFileURL(require.resolve('is-promise')).href.replace('.js', '.mjs')) | ||
const { default: isPromise, ...rest } = module | ||
@@ -56,0 +58,0 @@ assert.deepEqual(rest, {}) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
151253
53
3855