Repository: avajs/ava (js)

Issue:
Ava `test()` expression is not callable  / has no call signatures
I ran `npm ava init` in a project and created a first test as per ava docs.

Immediately the vscode editor is giving me a typescript warning error.

```
This expression is not callable.
  Type 'typeof import("/Users/dc/dev/exiteer/xbot/server/node_modules/ava/index")' has no call signatures.
```

![image](https://user-images.githubusercontent.com/514002/87449280-d57f6280-c5b1-11ea-8720-585b149888f3.png)

I did look if there were separate typings but there is a `index.d.ts` as part of the basic module that's installed.

> We'll also need your AVA configuration (in `package.json` or `ava.config.*` configuration files) and how you're invoking AVA. Share the installed AVA version (get it by running `npx ava --version`).

At this point i don't have any config files for ava, the initial install didn't seem to create.

FWIW this is a `js` file but I am leaving the typescript error checking running, i find it catches a lot of type errors even in plain JS files without type declarations.


Pull request description:
Add a mostly hidden test.default that provides test
Our type definition uses ESM syntax; when using CJS with VSCode, the auto-completion assumes the root is accessed through `require('ava').default`. Placate VSCode by adding a mostly hidden default property on the root. This is available through both CJS and ESM imports. We use a proxy so that we don't end up with root.default.default.default chains.

Fixes #2539.


Patch:
diff --git a/lib/create-chain.js b/lib/create-chain.js
index 7f283ad4..525fa96d 100644
--- a/lib/create-chain.js
+++ b/lib/create-chain.js
@@ -101,5 +101,28 @@ export default function createChain(fn, defaults, meta) {
 
 	root.meta = meta;
 
+	// Our type definition uses ESM syntax; when using CJS with VSCode, the
+	// auto-completion assumes the root is accessed through `require('ava').default`.
+	// Placate VSCode by adding a mostly hidden default property on the root.
+	// This is available through both CJS and ESM imports. We use a proxy so that
+	// we don't end up with root.default.default.default chains.
+	Object.defineProperty(root, 'default', {
+		configurable: false,
+		enumerable: false,
+		writable: false,
+		value: new Proxy(root, {
+			apply(target, thisArg, argumentsList) {
+				target.apply(thisArg, argumentsList);
+			},
+			get(target, prop) {
+				if (prop === 'default') {
+					throw new TypeError('Cannot access default.default');
+				}
+
+				return target[prop];
+			},
+		}),
+	});
+
 	return root;
 }


Test patch:
diff --git a/test/cjs-default/fixtures/package.json b/test/cjs-default/fixtures/package.json
new file mode 100644
index 00000000..82a86188
--- /dev/null
+++ b/test/cjs-default/fixtures/package.json
@@ -0,0 +1,7 @@
+{
+	"ava": {
+		"files": [
+			"*.cjs"
+		]
+	}
+}
diff --git a/test/cjs-default/fixtures/test.cjs b/test/cjs-default/fixtures/test.cjs
new file mode 100644
index 00000000..9cbaaecc
--- /dev/null
+++ b/test/cjs-default/fixtures/test.cjs
@@ -0,0 +1,19 @@
+const test = require('ava');
+
+test.default('callable', t => { // eslint-disable-line ava/no-unknown-modifiers
+	t.pass();
+});
+
+test('no recursion', t => {
+	t.throws(() => test.default.default, {
+		name: 'TypeError',
+	});
+});
+
+test('not enumerable', t => {
+	t.false(Object.keys(test).includes('default'));
+});
+
+test('main export equals the ESM export', async t => {
+	t.is(test, (await import('ava')).default); // eslint-disable-line node/no-unsupported-features/es-syntax
+});
diff --git a/test/cjs-default/test.js b/test/cjs-default/test.js
new file mode 100644
index 00000000..24c9fec5
--- /dev/null
+++ b/test/cjs-default/test.js
@@ -0,0 +1,7 @@
+import test from '@ava/test';
+
+import {fixture} from '../helpers/exec.js';
+
+test('ok', async t => {
+	await t.notThrowsAsync(fixture());
+});