diff --git a/packages/lwc/__tests__/default-exports.spec.ts b/packages/lwc/__tests__/default-exports.spec.ts new file mode 100644 index 0000000000..4b92f8e2d1 --- /dev/null +++ b/packages/lwc/__tests__/default-exports.spec.ts @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, Salesforce, Inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ +import * as fs from 'node:fs'; +import * as path from 'node:path'; + +const PACKAGE_ROOT = path.join(__dirname, '..'); + +const expectExportDefaultFromPackageInFile = (pkgName: string, ext: string) => { + const filename = path.join(PACKAGE_ROOT, pkgName + ext); + const contents = fs.readFileSync(filename, 'utf8'); + const exportDefaultFromPackage = new RegExp( + `^export \\{ default \\} from '@lwc/${pkgName}';$`, + 'm' + ); + expect(contents).toMatch(exportDefaultFromPackage); +}; + +/** + * Jest uses CommonJS, which means that packages with no explicit export statements actually export + * the default `module.exports` empty object. That export is an empty object with the prototype set + * to an empty object with null prototype. + */ +const hasExplicitDefaultExport = (mod: object) => { + // No default export = self explanatory + if (!('default' in mod)) return false; + // If we have more than one export, then we must have explicitly declared them + if (Object.keys(mod).length > 1) return true; + const def = mod.default; + // If it's not an object, it must be an explicit export + if (typeof def !== 'object' || def === null) return true; + // If it's not an empty object, it's not the placeholder object + if (Object.keys(def).length > 0) return true; + const proto = Object.getPrototypeOf(def); + // If the prototype isn't an empty null-prototype object, it's not the placeholder object + if (Object.keys(proto).length > 0 || Object.getPrototypeOf(proto) !== null) return true; + // It must be the placeholder object! + return false; +}; + +describe('default exports are not forgotten', () => { + const allFiles = fs.readdirSync(PACKAGE_ROOT); + const packages = allFiles + .filter((f) => f.endsWith('.js') && f !== 'index.js') + .map((f) => f.slice(0, -3)); + test.each(packages)('@lwc/%s', async (pkg) => { + const realModule = await import(`@lwc/${pkg}`); + // When jest properly supports ESM, this will be a lot simpler + // const aliasedModule = await import(`lwc/${pkg}`); + // expect(aliasedModule.default).toBe(realModule.default); + if (hasExplicitDefaultExport(realModule)) { + expectExportDefaultFromPackageInFile(pkg, '.d.ts'); + expectExportDefaultFromPackageInFile(pkg, '.js'); + } + }); +}); diff --git a/packages/lwc/__tests__/package-exports.spec.ts b/packages/lwc/__tests__/package-exports.spec.ts new file mode 100644 index 0000000000..e371e52cd8 --- /dev/null +++ b/packages/lwc/__tests__/package-exports.spec.ts @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024, Salesforce, Inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +describe('packaged dependencies are re-exported', () => { + const pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf8')); + test.each(Object.keys(pkg.dependencies))(`%s is exported`, (name) => { + const relative = name.replace('@lwc', '.'); + expect(pkg.exports[relative]).toBe(`${relative}.js`); + }); +}); diff --git a/packages/lwc/babel-plugin-component.d.ts b/packages/lwc/babel-plugin-component.d.ts index 13aedb122c..a100c15ff7 100644 --- a/packages/lwc/babel-plugin-component.d.ts +++ b/packages/lwc/babel-plugin-component.d.ts @@ -1,7 +1,8 @@ /* - * Copyright (c) 2023, salesforce.com, inc. + * Copyright (c) 2024, salesforce.com, inc. * All rights reserved. * SPDX-License-Identifier: MIT * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT */ export type * from '@lwc/babel-plugin-component'; +export { default } from '@lwc/babel-plugin-component'; diff --git a/packages/lwc/babel-plugin-component.js b/packages/lwc/babel-plugin-component.js index aa2783863b..94085f3c4e 100644 --- a/packages/lwc/babel-plugin-component.js +++ b/packages/lwc/babel-plugin-component.js @@ -1,7 +1,8 @@ /* - * Copyright (c) 2023, salesforce.com, inc. + * Copyright (c) 2024, salesforce.com, inc. * All rights reserved. * SPDX-License-Identifier: MIT * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT */ export * from '@lwc/babel-plugin-component'; +export { default } from '@lwc/babel-plugin-component'; diff --git a/packages/lwc/features.d.ts b/packages/lwc/features.d.ts index b741121fc3..227385377d 100644 --- a/packages/lwc/features.d.ts +++ b/packages/lwc/features.d.ts @@ -1,7 +1,8 @@ /* - * Copyright (c) 2023, salesforce.com, inc. + * Copyright (c) 2024, salesforce.com, inc. * All rights reserved. * SPDX-License-Identifier: MIT * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT */ export type * from '@lwc/features'; +export { default } from '@lwc/features'; diff --git a/packages/lwc/features.js b/packages/lwc/features.js index 2178f40b1b..9c1abd7f80 100644 --- a/packages/lwc/features.js +++ b/packages/lwc/features.js @@ -1,7 +1,8 @@ /* - * Copyright (c) 2023, salesforce.com, inc. + * Copyright (c) 2024, salesforce.com, inc. * All rights reserved. * SPDX-License-Identifier: MIT * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT */ export * from '@lwc/features'; +export { default } from '@lwc/features'; diff --git a/packages/lwc/jest.config.cjs b/packages/lwc/jest.config.cjs new file mode 100644 index 0000000000..4957ce1288 --- /dev/null +++ b/packages/lwc/jest.config.cjs @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024, Salesforce, Inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ +/* eslint-env node */ + +/** + * This is a .cjs file because this package is an ESM package, but jest needs CJS to work. + * If this file is ever changed to a .js file, add an exclusion to the package.json "files" list, + * so that this is not shipped as part of the package. + */ + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const BASE_CONFIG = require('../../scripts/jest/base.config'); + +module.exports = { + ...BASE_CONFIG, + displayName: 'lwc', + testEnvironment: 'jsdom', +}; diff --git a/packages/lwc/rollup-plugin.d.ts b/packages/lwc/rollup-plugin.d.ts index cd513f38ec..fcce3247f3 100644 --- a/packages/lwc/rollup-plugin.d.ts +++ b/packages/lwc/rollup-plugin.d.ts @@ -1,7 +1,8 @@ /* - * Copyright (c) 2023, salesforce.com, inc. + * Copyright (c) 2024, salesforce.com, inc. * All rights reserved. * SPDX-License-Identifier: MIT * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT */ export type * from '@lwc/rollup-plugin'; +export { default } from '@lwc/rollup-plugin'; diff --git a/packages/lwc/rollup-plugin.js b/packages/lwc/rollup-plugin.js index 9748cbf01d..7cf687875e 100644 --- a/packages/lwc/rollup-plugin.js +++ b/packages/lwc/rollup-plugin.js @@ -1,7 +1,8 @@ /* - * Copyright (c) 2023, salesforce.com, inc. + * Copyright (c) 2024, salesforce.com, inc. * All rights reserved. * SPDX-License-Identifier: MIT * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT */ export * from '@lwc/rollup-plugin'; +export { default } from '@lwc/rollup-plugin'; diff --git a/packages/lwc/template-compiler.d.ts b/packages/lwc/template-compiler.d.ts index 12e4e23f8d..2374ddec1c 100644 --- a/packages/lwc/template-compiler.d.ts +++ b/packages/lwc/template-compiler.d.ts @@ -1,7 +1,8 @@ /* - * Copyright (c) 2023, salesforce.com, inc. + * Copyright (c) 2024, salesforce.com, inc. * All rights reserved. * SPDX-License-Identifier: MIT * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT */ export type * from '@lwc/template-compiler'; +export { default } from '@lwc/template-compiler'; diff --git a/packages/lwc/template-compiler.js b/packages/lwc/template-compiler.js index b6e7499899..b10da3a71f 100644 --- a/packages/lwc/template-compiler.js +++ b/packages/lwc/template-compiler.js @@ -1,7 +1,8 @@ /* - * Copyright (c) 2023, salesforce.com, inc. + * Copyright (c) 2024, salesforce.com, inc. * All rights reserved. * SPDX-License-Identifier: MIT * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT */ export * from '@lwc/template-compiler'; +export { default } from '@lwc/template-compiler'; diff --git a/packages/lwc/tsconfig.json b/packages/lwc/tsconfig.json index cbf383840f..36ec8254e2 100644 --- a/packages/lwc/tsconfig.json +++ b/packages/lwc/tsconfig.json @@ -1,5 +1,7 @@ { + "extends": "../../tsconfig.json", + "compilerOptions": { - "lib": ["dom"] + "lib": ["dom", "es2021"] } } diff --git a/scripts/jest/root.config.js b/scripts/jest/root.config.js index b69e5198f1..bf1882fbd8 100644 --- a/scripts/jest/root.config.js +++ b/scripts/jest/root.config.js @@ -23,6 +23,7 @@ module.exports = { '/packages/@lwc/synthetic-shadow', '/packages/@lwc/template-compiler', '/packages/@lwc/wire-service', + '/packages/lwc', ], coverageThreshold: { global: {