diff --git a/.changeset/nextjs-cjs-build.md b/.changeset/nextjs-cjs-build.md new file mode 100644 index 000000000..e8c3f7b6f --- /dev/null +++ b/.changeset/nextjs-cjs-build.md @@ -0,0 +1,5 @@ +--- +'@asgardeo/nextjs': patch +--- + +Ship a working CommonJS build. The `require` entry (`dist/cjs/index.js`, used by Jest and other CommonJS consumers) only contained the three entry files and failed with `Cannot find module './AsgardeoNextClient'`; the ESM build only worked because `tsc` re-emitted every file on top of it. Every source file is now transpiled on its own for both formats (a bundle cannot keep the per-module `'use client'` / `'use server'` directives), `dist/cjs` carries a `package.json` with `"type": "commonjs"`, `tsc` only emits the declarations, the type-only re-exports of the client entry are marked as such, and the package's `types`, `homepage` and `repository` fields point at the right paths. diff --git a/packages/nextjs/esbuild.config.mjs b/packages/nextjs/esbuild.config.mjs index ecd8e433d..7bb64e3e7 100644 --- a/packages/nextjs/esbuild.config.mjs +++ b/packages/nextjs/esbuild.config.mjs @@ -16,11 +16,33 @@ * under the License. */ +import {mkdirSync, readdirSync, writeFileSync} from 'fs'; +import {join} from 'path'; import {build} from 'esbuild'; +/** + * Collects every source file under `directory`, skipping tests. + * + * Unlike the sibling packages this one cannot be bundled: Next.js needs the `'use client'` / + * `'use server'` directives on the module that defines each component or server action, and a + * bundle cannot keep them per module. Every file is therefore transpiled on its own and the + * output mirrors the `src` tree for both formats. + */ +const collectSourceFiles = directory => + readdirSync(directory, {withFileTypes: true}).flatMap(entry => { + const path = join(directory, entry.name); + + if (entry.isDirectory()) { + return entry.name === '__tests__' ? [] : collectSourceFiles(path); + } + + return /\.tsx?$/.test(entry.name) && !/\.(test|spec)\.tsx?$/.test(entry.name) ? [path] : []; + }); + const commonOptions = { bundle: false, - entryPoints: ['src/index.ts', 'src/server/index.ts', 'src/middleware.ts'], + entryPoints: collectSourceFiles('src'), + outbase: 'src', platform: 'node', target: ['node18'], }; @@ -38,3 +60,7 @@ await build({ outdir: 'dist/cjs', sourcemap: true, }); + +// The package is `"type": "module"`, so Node would otherwise parse the CommonJS output as ESM. +mkdirSync('dist/cjs', {recursive: true}); +writeFileSync('dist/cjs/package.json', `${JSON.stringify({type: 'commonjs'}, null, 2)}\n`); diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 7d4d4367e..e8506d543 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -8,7 +8,7 @@ "react", "ssr" ], - "homepage": "https://github.com/asgardeo/javascript/tree/main/packages/next#readme", + "homepage": "https://github.com/asgardeo/javascript/tree/main/packages/nextjs#readme", "bugs": { "url": "https://github.com/asgardeo/javascript/issues" }, @@ -41,14 +41,14 @@ "README.md", "LICENSE" ], - "types": "dist/index.d.ts", + "types": "dist/types/index.d.ts", "repository": { "type": "git", "url": "https://github.com/asgardeo/javascript", - "directory": "packages/next" + "directory": "packages/nextjs" }, "scripts": { - "build": "pnpm clean && node esbuild.config.mjs && tsc -p tsconfig.lib.json --outDir dist/esm", + "build": "pnpm clean && node esbuild.config.mjs && tsc -p tsconfig.lib.json --emitDeclarationOnly", "clean": "rimraf dist", "fix:lint": "eslint . --ext .js,.jsx,.ts,.tsx,.cjs,.mjs", "lint": "eslint . --ext .js,.jsx,.ts,.tsx,.cjs,.mjs", diff --git a/packages/nextjs/src/client/index.ts b/packages/nextjs/src/client/index.ts index 1320a994a..6b696f576 100644 --- a/packages/nextjs/src/client/index.ts +++ b/packages/nextjs/src/client/index.ts @@ -19,22 +19,22 @@ export {default as useAsgardeo} from './contexts/Asgardeo/useAsgardeo'; export {default as Organization} from './components/presentation/Organization/Organization'; -export {OrganizationProps} from './components/presentation/Organization/Organization'; +export type {OrganizationProps} from './components/presentation/Organization/Organization'; export {default as CreateOrganization} from './components/presentation/CreateOrganization/CreateOrganization'; -export {CreateOrganizationProps} from './components/presentation/CreateOrganization/CreateOrganization'; +export type {CreateOrganizationProps} from './components/presentation/CreateOrganization/CreateOrganization'; export {default as OrganizationProfile} from './components/presentation/OrganizationProfile/OrganizationProfile'; -export {OrganizationProfileProps} from './components/presentation/OrganizationProfile/OrganizationProfile'; +export type {OrganizationProfileProps} from './components/presentation/OrganizationProfile/OrganizationProfile'; export {default as OrganizationSwitcher} from './components/presentation/OrganizationSwitcher/OrganizationSwitcher'; -export {OrganizationSwitcherProps} from './components/presentation/OrganizationSwitcher/OrganizationSwitcher'; +export type {OrganizationSwitcherProps} from './components/presentation/OrganizationSwitcher/OrganizationSwitcher'; export {default as SignedIn} from './components/control/SignedIn/SignedIn'; -export {SignedInProps} from './components/control/SignedIn/SignedIn'; +export type {SignedInProps} from './components/control/SignedIn/SignedIn'; export {default as SignedOut} from './components/control/SignedOut/SignedOut'; -export {SignedOutProps} from './components/control/SignedOut/SignedOut'; +export type {SignedOutProps} from './components/control/SignedOut/SignedOut'; export {default as SignInButton} from './components/actions/SignInButton/SignInButton'; export type {SignInButtonProps} from './components/actions/SignInButton/SignInButton';