Skip to content

Commit

Permalink
Merge pull request #68 from iambumblehead/update-hooks-usage-to-avoid…
Browse files Browse the repository at this point in the history
…-recursive-loop

add initial support for typescript, resolve load hook recursion error
  • Loading branch information
iambumblehead authored Jul 20, 2022
2 parents db63e0e + c696f55 commit 312f7ab
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 19 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# changelog

* 1.8.1 _Jul.19.2022_
* resolve recursive load hook crash when using esmock with other loader
* add basic typescript support, using ts-node/esm
* add examples and tests showing how to use with ts loader
* 1.8.0 _Jul.18.2022_
* use strict-mocking behaviour by default, "partial mock" is optional
* 1.7.8 _Jul.16.2022_
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
"test-tap": "NODE_OPTIONS=--loader=esmock tap",
"test-uvu": "NODE_OPTIONS=--loader=esmock uvu spec",
"test-ava": "NODE_OPTIONS=--loader=esmock ava",
"test-mocha": "mocha --loader=esmock"
"test-mocha": "mocha --loader=esmock",
"test-ts": "node --loader=ts-node/esm --loader=esmock --test *ts"
}
}
```
Expand Down
11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
{
"name": "esmock",
"version": "1.8.0",
"version": "1.8.1",
"license": "MIT",
"readmeFilename": "README.md",
"description": "provides native ESM import mocking for unit tests",
"author": "chris <[email protected]>",
"main": "src/esmockLoader.mjs",
"module": "src/esmockLoader.mjs",
"type": "module",
"types": "src/esmock.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/iambumblehead/esmock.git"
Expand Down Expand Up @@ -57,17 +58,21 @@
"uvu": "0.5.3",
"ava": "^4.0.1",
"eslint": "^8.12.0",
"ts-node": "^10.9.1",
"esmock": "file:.",
"form-urlencoded": "4.2.1",
"run-script-os": "^1.1.6",
"sinon": "^12.0.1"
},
"scripts": {
"test-node-esbuildts": "node --no-warnings --loader=@esbuild-kit/esm-loader --loader=esmock --test ./spec/node-ts/*ts",
"test-node-ts": "node --no-warnings --loader=ts-node/esm --loader=esmock --test ./spec/node-ts/*ts",
"test-node": "node --no-warnings --loader=esmock --test ./spec/node/",
"test-node18": "if (node -v | grep v18); then npm run test-node; fi;",
"test-node18": "npm run test-node && npm run test-node-ts",
"test-nodeis18": "if (node -v | grep v18); then npm run test-node18; fi;",
"test-ava": "npx ava --node-arguments=\"--loader=esmock\" ./spec/ava/*.spec.js",
"test-uvu": "node --loader=esmock ./node_modules/uvu/bin.js ./spec/uvu/",
"test:default": "npm run test-node18 && npm run test-ava && npm run test-uvu",
"test:default": "npm run test-nodeis18 && npm run test-ava && npm run test-uvu",
"test:windows": "npm run test-ava && npm run test-uvu",
"test": "run-script-os",
"test-no-warn": "npx ava --node-arguments=\"--loader=esmock --no-warnings\"",
Expand Down
5 changes: 5 additions & 0 deletions spec/local/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import path from 'path';

export default {
pathbasenamewrap: (n: any) => path.basename(n)
};
14 changes: 14 additions & 0 deletions spec/node-ts/esmock.node-ts.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import test from 'node:test';
import assert from 'assert';
import esmock from '../../src/esmock.js';

test('should mock ts when using node-ts', { only : true }, async () => {
const main = await esmock('../local/main.ts', {
path: {
basename: () => 'hellow'
}
});

assert.strictEqual( main.pathbasenamewrap(), 'hellow' );
assert.ok(true);
});
2 changes: 2 additions & 0 deletions src/esmock.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare function esmock(path: string, localmock?: any, globalmock?: any): any;
export = esmock;
3 changes: 2 additions & 1 deletion src/esmock.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ const esmock = async (modulePath, mockDefs, globalDefs, opt = {}, err) => {
const calleePath = (err || new Error()).stack.split('\n')[2]
.replace(/^.*file:\/\//, '') // rm every before filepath
.replace(/:[\d]*:[\d]*.*$/, '') // rm line and row number
.replace(/^.*:/, ''); // rm windows-style drive location
.replace(/^.*:/, '') // rm windows-style drive location
.replace(/.*at [^(]*\(/, ''); // rm ' at TestContext.<anonymous> ('

if (!global.esmockloader)
throw new Error('process must be started with --loader=esmock');
Expand Down
4 changes: 4 additions & 0 deletions src/esmockIsLoader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default process.execArgv
.some(args => args.startsWith('--loader=') && args.includes('esmock'))
|| /(?:^|\s)?--(?:experimental-)?loader=(["']*)esmock\1(?:\s|$)/
.test(process.env.NODE_OPTIONS);
36 changes: 22 additions & 14 deletions src/esmockLoader.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,9 @@ import path from 'path';
import url from 'url';

import esmock from './esmock.js';
import esmockIsLoader from './esmockIsLoader.js';

global.esmockloader = global.esmockloader || (
process.execArgv.some(
args => (args.startsWith('--loader=') && args.includes('esmock'))
)
) || (
/(?:^|\s)?--(?:experimental-)?loader=(["']*)esmock\1(?:\s|$)/.test(
process.env.NODE_OPTIONS
)
);
global.esmockloader = esmockIsLoader;

export default esmock;

Expand All @@ -30,7 +23,7 @@ const esmockKeyRe = /esmockKey=\d*/;
const withHashRe = /[^#]*#/;
const isesmRe = /isesm=true/;

const resolve = async (specifier, context, defaultResolve) => {
const resolve = async (specifier, context, nextResolve) => {
const { parentURL } = context;
const [ esmockKeyParamSmall ] =
(parentURL && parentURL.match(/\?esmk=\d*/)) || [];
Expand All @@ -40,10 +33,25 @@ const resolve = async (specifier, context, defaultResolve) => {
const [ esmockKeyParam ] =
(esmockKeyLong && esmockKeyLong.match(esmockKeyRe) || []);

// new versions of node: when multiple loaders are used and context
// is passed to nextResolve, the process crashes in a recursive call
// see: /esmock/issues/#48
//
// old versions of node: if context.parentURL is defined, and context
// is not passed to nextResolve, the tests fail
//
// later versions of node v16 include 'node-addons'
const resolved = context.conditions.slice(-1)[0] === 'node-addons'
? ((context.importAssertions && context.parentURL)
? await nextResolve(specifier, context)
: await nextResolve(specifier))
: (context.parentURL
? await nextResolve(specifier, context)
: await nextResolve(specifier));

if (!esmockKeyParam)
return defaultResolve(specifier, context, defaultResolve);
return resolved;

const resolved = await defaultResolve(specifier, context, defaultResolve);
const resolvedurl = decodeURI(resolved.url);
const moduleKeyRe = new RegExp(
'.*(' + resolvedurl + '\\?' + esmockKeyParam + '[^#]*).*');
Expand Down Expand Up @@ -71,7 +79,7 @@ const resolve = async (specifier, context, defaultResolve) => {

const load = async (url, context, nextLoad) => {
if (esmockModuleKeysRe.test(url)) // parent of mocked modules
return nextLoad(url, context, nextLoad);
return nextLoad(url, context);

url = url.replace(esmockGlobalsAndAfterRe, '');
if (url.startsWith(urlDummy)) {
Expand All @@ -92,7 +100,7 @@ const load = async (url, context, nextLoad) => {
};
}

return nextLoad(url, context, nextLoad);
return nextLoad(url, context);
};

// node lt 16.12 require getSource, node gte 16.12 warn remove getSource
Expand Down
8 changes: 8 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"esm": true,
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"module": "ESNext",
"moduleResolution": "node"
}
}

0 comments on commit 312f7ab

Please sign in to comment.