Skip to content

Commit

Permalink
fix: allow cjs version of packages to export a default value (#943)
Browse files Browse the repository at this point in the history
* chore: allow csj to export a default value

* ignore tsup.config

* suggestion
  • Loading branch information
straker committed Nov 30, 2023
1 parent 4367556 commit b5aee34
Show file tree
Hide file tree
Showing 16 changed files with 189 additions and 32 deletions.
22 changes: 18 additions & 4 deletions packages/playwright/test/commonjsTest.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
// ensure backwards compatibility of commonJs format
const defaultExport = require('../dist/index.js').default;
const implicitDefaultExport = require('../dist/index.js');
const explicitDefaultExport = require('../dist/index.js').default;
const { AxeBuilder } = require('../dist/index.js');
const assert = require('assert');

assert(typeof defaultExport === 'function', 'default export is not a function');
assert(typeof AxeBuilder === 'function', 'named export is not a function');

assert(
typeof implicitDefaultExport === 'function',
'implicit default export is not a function'
);
assert(
implicitDefaultExport === AxeBuilder,
'implicit default and named export are not the same'
);

assert(
typeof explicitDefaultExport === 'function',
'explicit default export is not a function'
);
assert(
defaultExport === AxeBuilder,
'default and named export are not the same'
explicitDefaultExport === AxeBuilder,
'explicit default and named export are not the same'
);
6 changes: 6 additions & 0 deletions packages/playwright/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { defineConfig } from 'tsup';
import { esbuildPluginCJSInterop } from '../../utils/esbuild-plugin-cjs-interop.js';

export default defineConfig({
esbuildPlugins: [esbuildPluginCJSInterop]
});
22 changes: 18 additions & 4 deletions packages/puppeteer/test/commonjsTest.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
// ensure backwards compatibility of commonJs format
const defaultExport = require('../dist/index.js').default;
const implicitDefaultExport = require('../dist/index.js'); // support <4.7.3
const explicitDefaultExport = require('../dist/index.js').default; // support 4.7.3+
const { AxePuppeteer } = require('../dist/index.js');
const assert = require('assert');

assert(typeof defaultExport === 'function', 'default export is not a function');
assert(typeof AxePuppeteer === 'function', 'named export is not a function');

assert(
typeof implicitDefaultExport === 'function',
'implicit default export is not a function'
);
assert(
implicitDefaultExport === AxePuppeteer,
'implicit default and named export are not the same'
);

assert(
typeof explicitDefaultExport === 'function',
'explicit default export is not a function'
);
assert(
defaultExport === AxePuppeteer,
'default and named export are not the same'
explicitDefaultExport === AxePuppeteer,
'explicit default and named export are not the same'
);
6 changes: 6 additions & 0 deletions packages/puppeteer/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { defineConfig } from 'tsup';
import { esbuildPluginCJSInterop } from '../../utils/esbuild-plugin-cjs-interop.js';

export default defineConfig({
esbuildPlugins: [esbuildPluginCJSInterop]
});
8 changes: 1 addition & 7 deletions packages/react/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ function addComponent(component: any): void {
* Log axe violations to console.
* @param {AxeResults} results
*/
function logToConsole(results: axeCore.AxeResults): void {
export function logToConsole(results: axeCore.AxeResults): void {
console.group('%cNew axe issues', serious);
results.violations.forEach(result => {
let fmt: string;
Expand Down Expand Up @@ -411,9 +411,3 @@ export default function reactAxe(

return checkAndReport(document.body, timeout);
}

reactAxe.logToConsole = logToConsole;

if (typeof module === 'object') {
exports = module.exports = reactAxe;
}
23 changes: 21 additions & 2 deletions packages/react/test/commonjsTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,26 @@
global.window = {};
global.document = {};

const defaultExport = require('../dist/index.js');
const implicitDefaultExport = require('../dist/index.js');
const explicitDefaultExport = require('../dist/index.js').default;
const { logToConsole } = require('../dist/index.js');
const assert = require('assert');

assert(typeof defaultExport === 'function', 'export is not a function');
assert(
typeof implicitDefaultExport === 'function',
'implicit default export is not a function'
);

assert(
typeof explicitDefaultExport === 'function',
'explicit default export is not a function'
);
assert(
explicitDefaultExport === implicitDefaultExport,
'explicit default and named export are not the same'
);

assert(
typeof logToConsole === 'function',
'logToConsole export is not a function'
);
2 changes: 2 additions & 0 deletions packages/react/test/esmTest.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
// Setting them in this file will not work.
import './setupGlobals.mjs';
import defaultExport from '../dist/index.mjs';
import { logToConsole } from '../dist/index.mjs';
import assert from 'assert';

const exportIsFunction = typeof defaultExport === 'function';
assert(exportIsFunction, 'export is not a function');
assert(typeof logToConsole === 'function', 'logToConsole export is not a function');
2 changes: 1 addition & 1 deletion packages/react/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@
"resolveJsonModule": true,
"esModuleInterop": true
},
"exclude": ["test", "dist", "node_modules"]
"exclude": ["test", "dist", "node_modules", "tsup.config.ts"]
}
6 changes: 6 additions & 0 deletions packages/react/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { defineConfig } from 'tsup';
import { esbuildPluginCJSInterop } from '../../utils/esbuild-plugin-cjs-interop.js';

export default defineConfig({
esbuildPlugins: [esbuildPluginCJSInterop]
});
18 changes: 15 additions & 3 deletions packages/reporter-earl/tests/commonjsTest.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
// ensure backwards compatibility of commonJs format
const defaultExport = require('../dist/axeReporterEarl.js').default;
const implicitDefaultExport = require('../dist/axeReporterEarl.js');
const explicitDefaultExport = require('../dist/axeReporterEarl.js').default;
const assert = require('assert');

const exportIsFunction = typeof defaultExport === 'function';
assert(exportIsFunction, 'export is not a function');
assert(
typeof implicitDefaultExport === 'function',
'implicit default export is not a function'
);

assert(
typeof explicitDefaultExport === 'function',
'explicit default export is not a function'
);
assert(
explicitDefaultExport === implicitDefaultExport,
'explicit default and named export are not the same'
);
6 changes: 6 additions & 0 deletions packages/reporter-earl/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { defineConfig } from 'tsup';
import { esbuildPluginCJSInterop } from '../../utils/esbuild-plugin-cjs-interop.js';

export default defineConfig({
esbuildPlugins: [esbuildPluginCJSInterop]
});
22 changes: 18 additions & 4 deletions packages/webdriverio/test/commonjsTest.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
// ensure backwards compatibility of commonJs format
const defaultExport = require('../dist/index.js').default;
const implicitDefaultExport = require('../dist/index.js');
const explicitDefaultExport = require('../dist/index.js').default;
const { AxeBuilder } = require('../dist/index.js');
const assert = require('assert');

assert(typeof defaultExport === 'function', 'default export is not a function');
assert(typeof AxeBuilder === 'function', 'named export is not a function');

assert(
typeof implicitDefaultExport === 'function',
'implicit default export is not a function'
);
assert(
implicitDefaultExport === AxeBuilder,
'implicit default and named export are not the same'
);

assert(
typeof explicitDefaultExport === 'function',
'explicit default export is not a function'
);
assert(
defaultExport === AxeBuilder,
'default and named export are not the same'
explicitDefaultExport === AxeBuilder,
'explicit default and named export are not the same'
);
6 changes: 6 additions & 0 deletions packages/webdriverio/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { defineConfig } from 'tsup';
import { esbuildPluginCJSInterop } from '../../utils/esbuild-plugin-cjs-interop.js';

export default defineConfig({
esbuildPlugins: [esbuildPluginCJSInterop]
});
7 changes: 0 additions & 7 deletions packages/webdriverjs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,11 +291,4 @@ export default class AxeBuilder {
}
}

// ensure backwards compatibility with commonJs default export
if (typeof module === 'object') {
module.exports = AxeBuilder;
module.exports.default = AxeBuilder;
module.exports.AxeBuilder = AxeBuilder;
}

export { AxeBuilder };
6 changes: 6 additions & 0 deletions packages/webdriverjs/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { defineConfig } from 'tsup';
import { esbuildPluginCJSInterop } from '../../utils/esbuild-plugin-cjs-interop.js';

export default defineConfig({
esbuildPlugins: [esbuildPluginCJSInterop]
});
59 changes: 59 additions & 0 deletions utils/esbuild-plugin-cjs-interop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import path from 'path';

/**
* "Fixes" the esbuild problem of exporting the CJS default export as `module.exports.default`
* instead of `module.exports`. The plugin appends a block of code to the file that takes the
* `.default` module export and re-exports it as `module.exports`. It then takes all named
* exports and re-exports them as part of the `module.exports` under the same name. This also
* gives the benefit of exporting the `.default` module which allows us to support all 3 export
* styles: the default export, `.default` export, and named exports.
*
* @example
* // file.ts
* export default function myFun() {}
* export const PAGE_STATE = 1
*
* // index.cjs
* // Run-time. all are valid and work
* const implicitDefaultExport = require('./dist/file.js')
* const explicitDefaultExport = require('./dist/file.js').default
* const { PAGE_STATE as namedExport } = require('./dist/file.js')
*/
export const esbuildPluginCJSInterop = {
name: 'cjs-interop',
setup(build) {
build.onEnd(result => {
if (build.initialOptions.format === 'cjs') {
result.outputFiles.forEach(file => {
// make sure we're working with a js/cjs file specifically
if (!['.js', '.cjs'].includes(path.extname(file.path))) {
return;
}

// merge contents with plugin code
const contents = new Uint8Array(
file.contents.length + pluginCode.length
);
contents.set(file.contents);
contents.set(pluginCode, file.contents.length);
file.contents = contents;
});
}
});
}
};

const pluginCode = new TextEncoder().encode(`
if (module.exports.default) {
var ___default_export = module.exports.default;
var ___export_entries = Object.entries(module.exports);
module.exports = ___default_export;
___export_entries.forEach(([key, value]) => {
if (module.exports[key]) {
throw new Error(\`Export "\${key}" already exists on default export\`);
}
module.exports[key] = value;
});
}
`);

0 comments on commit b5aee34

Please sign in to comment.