diff --git a/.changeset/strong-otters-brush.md b/.changeset/strong-otters-brush.md new file mode 100644 index 0000000..016b231 --- /dev/null +++ b/.changeset/strong-otters-brush.md @@ -0,0 +1,11 @@ +--- +"single-spa-react": major +--- + +### Fixed + +- Enhanced compatibility with various bundlers and TypeScript `moduleResolution` strategies. The package's export patterns have been refined to address issues previously encountered with different bundling tools, ensuring more consistent and reliable integration across diverse build environments. Additionally, TypeScript type definitions have been improved, enhancing type safety and developer experience in varied TypeScript setups. + +### BREAKING CHANGES + +- The changes in export patterns / types may require updates in how projects import from `single-spa-react/*`. diff --git a/.gitignore b/.gitignore index bd45e52..b4c589e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,4 @@ node_modules/ .DS_Store lib/ yarn-error.log -coverage -parcel/ +coverage \ No newline at end of file diff --git a/package.json b/package.json index 38ff280..fc0690f 100644 --- a/package.json +++ b/package.json @@ -8,28 +8,39 @@ "exports": { "./package.json": "./package.json", ".": { - "import": "./lib/esm/single-spa-react.js", - "require": "./lib/cjs/single-spa-react.cjs" + "import": { + "types": "./types/single-spa-react.d.ts", + "default": "./lib/esm/single-spa-react.js" + }, + "require": { + "types": "./types/single-spa-react.d.cts", + "default": "./lib/cjs/single-spa-react.cjs" + } }, "./parcel": { - "import": "./lib/esm/parcel.js", - "require": "./lib/cjs/parcel.cjs" + "import": { + "types": "./types/parcel/index.d.ts", + "default": "./lib/esm/parcel.js" + }, + "require": { + "types": "./types/parcel/index.d.cts", + "default": "./lib/cjs/parcel.cjs" + } } }, + "types": "types/single-spa-react.d.cts", "files": [ "lib", "parcel", - "types/single-spa-react.d.ts", + "types", "README.md" ], "tsd": { "directory": "src" }, - "types": "types/single-spa-react.d.ts", "scripts": { "build": "concurrently pnpm:build:*", "build:rollup": "rollup -c", - "build:types": "rimraf parcel && copyfiles -f types/parcel/index.d.ts ./parcel", "lint": "eslint src", "test": "concurrently pnpm:test:*", "test:browser": "cross-env BABEL_ENV=test NODE_OPTIONS=--experimental-vm-modules jest --coverage", diff --git a/parcel/README.md b/parcel/README.md new file mode 100644 index 0000000..da90ec7 --- /dev/null +++ b/parcel/README.md @@ -0,0 +1,20 @@ +# Parcel Folder + +## Overview +This folder contains a `package.json` stub that redirects module resolutions, following the `package-json-redirects` strategy. + +## Why It's Here +Serves as a compatibility layer for Node 10, which does not support the `exports` field in `package.json`. A separate `package.json` with `main` and `types` fields directs Node 10's resolution strategy to the appropriate files. + +```json +{ + "main": "./lib/cjs/parcel.cjs", + "types": "./types/parcel/index.d.cts" +} +``` + +## How It Works +When Node 10 attempts to import from this folder, it consults the `main` and `types` fields in `package.json` to locate the actual implementation and types files. This facilitates keeping those files in separate subfolders while making them accessible to older Node versions. + +## Reference +For more detailed information and examples, see [this GitHub repository](https://github.com/andrewbranch/example-subpath-exports-ts-compat/tree/main/examples/node_modules/package-json-redirects). diff --git a/parcel/package.json b/parcel/package.json new file mode 100644 index 0000000..d5b43cb --- /dev/null +++ b/parcel/package.json @@ -0,0 +1,4 @@ +{ + "main": "../lib/umd/parcel.js", + "types": "../types/parcel/index.d.cts" +} \ No newline at end of file diff --git a/src/single-spa-react.js b/src/single-spa-react.js index faab99a..64a5cd8 100644 --- a/src/single-spa-react.js +++ b/src/single-spa-react.js @@ -48,7 +48,7 @@ const defaultOpts = { unmountResolves: {}, }; -export default function singleSpaReact(userOpts) { +function singleSpaReact(userOpts) { if (typeof userOpts !== "object") { throw new Error(`single-spa-react requires a configuration object`); } @@ -375,3 +375,5 @@ function createSingleSpaRoot(opts) { return SingleSpaRoot; } + +export default singleSpaReact; diff --git a/types/parcel/index.d.cts b/types/parcel/index.d.cts new file mode 100644 index 0000000..0672b54 --- /dev/null +++ b/types/parcel/index.d.cts @@ -0,0 +1,36 @@ +import * as React from "react"; +import { + ParcelConfig, + ParcelProps, + Parcel as SingleSpaParcel, +} from "single-spa"; + +interface ParcelCompProps { + config: ParcelConfig; + mountParcel?: ( + parcelConfig: ParcelConfig, + parcelProps: ParcelProps & ExtraProps + ) => SingleSpaParcel; + wrapWith?: string; + wrapStyle?: React.CSSProperties; + wrapClassName?: string; + appendTo?: HTMLElement; + parcelDidMount?: () => any; + handleError?: (err: Error) => any; + [extraProp: string]: any; +} + +interface ParcelState { + hasError: boolean; +} + +declare class Parcel extends React.Component< + ParcelCompProps, + ParcelState +> {} + +export = Parcel; + +declare namespace Parcel { + export { ParcelCompProps, ParcelState }; +} diff --git a/types/single-spa-react.d.cts b/types/single-spa-react.d.cts new file mode 100644 index 0000000..542ee35 --- /dev/null +++ b/types/single-spa-react.d.cts @@ -0,0 +1,63 @@ +import * as React from "react"; +import { AppProps, CustomProps, LifeCycleFn } from "single-spa"; + +declare const SingleSpaContext: React.Context; + +type DeprecatedRenderTypes = + | "createBlockingRoot" + | "unstable_createRoot" + | "unstable_createBlockingRoot"; + +type LegacyRenderType = "hydrate" | "render"; + +type RenderType = "createRoot" | "hydrateRoot" | LegacyRenderType; + +interface SingleSpaReactOpts { + React: any; + ReactDOM?: { + [T in LegacyRenderType]?: any; + }; + ReactDOMClient?: { + [T in RenderType]?: any; + }; + rootComponent?: + | React.ComponentClass + | React.FunctionComponent; + loadRootComponent?: ( + props?: RootComponentProps + ) => Promise>; + errorBoundary?: ( + err: Error, + errInfo: React.ErrorInfo, + props: RootComponentProps + ) => React.ReactElement; + errorBoundaryClass?: React.ComponentClass; + parcelCanUpdate?: boolean; + suppressComponentDidCatchWarning?: boolean; + domElementGetter?: (props: RootComponentProps) => HTMLElement; + renderType?: RenderType | (() => RenderType); +} + +interface ReactAppOrParcel { + bootstrap: LifeCycleFn; + mount: LifeCycleFn; + unmount: LifeCycleFn; + update?: LifeCycleFn; +} + +declare function singleSpaReact( + opts: SingleSpaReactOpts +): ReactAppOrParcel; + +export = singleSpaReact; + +declare namespace singleSpaReact { + export { + SingleSpaContext, + DeprecatedRenderTypes, + LegacyRenderType, + RenderType, + SingleSpaReactOpts, + ReactAppOrParcel, + }; +}