diff --git a/packages/playwright/test/commonjsTest.js b/packages/playwright/test/commonjsTest.js index 399ed177..893ebfbe 100644 --- a/packages/playwright/test/commonjsTest.js +++ b/packages/playwright/test/commonjsTest.js @@ -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' ); diff --git a/packages/playwright/tsup.config.ts b/packages/playwright/tsup.config.ts new file mode 100644 index 00000000..03ad4e61 --- /dev/null +++ b/packages/playwright/tsup.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'tsup'; +import { esbuildPluginCJSInterop } from '../../utils/esbuild-plugin-cjs-interop.js'; + +export default defineConfig({ + esbuildPlugins: [esbuildPluginCJSInterop] +}); diff --git a/packages/puppeteer/test/commonjsTest.js b/packages/puppeteer/test/commonjsTest.js index c051ef53..723ac2d0 100644 --- a/packages/puppeteer/test/commonjsTest.js +++ b/packages/puppeteer/test/commonjsTest.js @@ -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' ); diff --git a/packages/puppeteer/tsup.config.ts b/packages/puppeteer/tsup.config.ts new file mode 100644 index 00000000..03ad4e61 --- /dev/null +++ b/packages/puppeteer/tsup.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'tsup'; +import { esbuildPluginCJSInterop } from '../../utils/esbuild-plugin-cjs-interop.js'; + +export default defineConfig({ + esbuildPlugins: [esbuildPluginCJSInterop] +}); diff --git a/packages/react/index.ts b/packages/react/index.ts index 008a7513..916f904f 100644 --- a/packages/react/index.ts +++ b/packages/react/index.ts @@ -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; @@ -411,9 +411,3 @@ export default function reactAxe( return checkAndReport(document.body, timeout); } - -reactAxe.logToConsole = logToConsole; - -if (typeof module === 'object') { - exports = module.exports = reactAxe; -} diff --git a/packages/react/test/commonjsTest.js b/packages/react/test/commonjsTest.js index b4732c0a..e481c5dd 100644 --- a/packages/react/test/commonjsTest.js +++ b/packages/react/test/commonjsTest.js @@ -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' +); diff --git a/packages/react/test/esmTest.mjs b/packages/react/test/esmTest.mjs index f1ef59f7..284d8be8 100644 --- a/packages/react/test/esmTest.mjs +++ b/packages/react/test/esmTest.mjs @@ -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'); diff --git a/packages/react/tsconfig.json b/packages/react/tsconfig.json index 67bc4ad4..7d9a9a54 100644 --- a/packages/react/tsconfig.json +++ b/packages/react/tsconfig.json @@ -12,5 +12,5 @@ "resolveJsonModule": true, "esModuleInterop": true }, - "exclude": ["test", "dist", "node_modules"] + "exclude": ["test", "dist", "node_modules", "tsup.config.ts"] } diff --git a/packages/react/tsup.config.ts b/packages/react/tsup.config.ts new file mode 100644 index 00000000..03ad4e61 --- /dev/null +++ b/packages/react/tsup.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'tsup'; +import { esbuildPluginCJSInterop } from '../../utils/esbuild-plugin-cjs-interop.js'; + +export default defineConfig({ + esbuildPlugins: [esbuildPluginCJSInterop] +}); diff --git a/packages/reporter-earl/tests/commonjsTest.js b/packages/reporter-earl/tests/commonjsTest.js index 6bf9a838..ac994bb8 100644 --- a/packages/reporter-earl/tests/commonjsTest.js +++ b/packages/reporter-earl/tests/commonjsTest.js @@ -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' +); diff --git a/packages/reporter-earl/tsup.config.ts b/packages/reporter-earl/tsup.config.ts new file mode 100644 index 00000000..03ad4e61 --- /dev/null +++ b/packages/reporter-earl/tsup.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'tsup'; +import { esbuildPluginCJSInterop } from '../../utils/esbuild-plugin-cjs-interop.js'; + +export default defineConfig({ + esbuildPlugins: [esbuildPluginCJSInterop] +}); diff --git a/packages/webdriverio/test/commonjsTest.js b/packages/webdriverio/test/commonjsTest.js index 399ed177..893ebfbe 100644 --- a/packages/webdriverio/test/commonjsTest.js +++ b/packages/webdriverio/test/commonjsTest.js @@ -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' ); diff --git a/packages/webdriverio/tsup.config.ts b/packages/webdriverio/tsup.config.ts new file mode 100644 index 00000000..03ad4e61 --- /dev/null +++ b/packages/webdriverio/tsup.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'tsup'; +import { esbuildPluginCJSInterop } from '../../utils/esbuild-plugin-cjs-interop.js'; + +export default defineConfig({ + esbuildPlugins: [esbuildPluginCJSInterop] +}); diff --git a/packages/webdriverjs/src/index.ts b/packages/webdriverjs/src/index.ts index c1aab37b..8d035816 100644 --- a/packages/webdriverjs/src/index.ts +++ b/packages/webdriverjs/src/index.ts @@ -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 }; diff --git a/packages/webdriverjs/tsup.config.ts b/packages/webdriverjs/tsup.config.ts new file mode 100644 index 00000000..03ad4e61 --- /dev/null +++ b/packages/webdriverjs/tsup.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'tsup'; +import { esbuildPluginCJSInterop } from '../../utils/esbuild-plugin-cjs-interop.js'; + +export default defineConfig({ + esbuildPlugins: [esbuildPluginCJSInterop] +}); diff --git a/utils/esbuild-plugin-cjs-interop.ts b/utils/esbuild-plugin-cjs-interop.ts new file mode 100644 index 00000000..34cebdd6 --- /dev/null +++ b/utils/esbuild-plugin-cjs-interop.ts @@ -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; + }); +} +`);