Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ for css_file in "${SHARED_CSS_FILES[@]}" "${SCREEN_CSS_FILES[@]}"; do
fi
done

# Add JS files in correct dependency order: main → react-vendor (React + React-DOM + Base UI) → vendor → common → screen-specific
# Add JS files in correct dependency order: react-vendor → vendor → common → screen-specific → main
# First collect files by type
MAIN_JS_FILE=""
REACT_VENDOR_JS_FILE=""
Expand Down Expand Up @@ -71,8 +71,8 @@ for js_file in "${SHARED_JS_FILES[@]}"; do
fi
done

# Add scripts in dependency order: main → react-vendor (React + Base UI) → vendor → common → screen entry
for js_file in "$MAIN_JS_FILE" "$REACT_VENDOR_JS_FILE" "$VENDOR_JS_FILE" "$COMMON_JS_FILE" "$SCREEN_ENTRY_FILE"; do
# Add scripts in dependency order: react-vendor → vendor → common → screen entry → main
for js_file in "$REACT_VENDOR_JS_FILE" "$VENDOR_JS_FILE" "$COMMON_JS_FILE" "$SCREEN_ENTRY_FILE" "$MAIN_JS_FILE"; do
if [ -z "$js_file" ]; then continue; fi
js_basename=$(basename "$js_file")
if [[ ! "$js_basename" =~ ^[^a-zA-Z0-9] ]]; then
Expand Down
8 changes: 4 additions & 4 deletions react-js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,15 @@ dist/
├── main.[hash].js # Main application bundle
├── shared/
│ ├── style.[hash].css # Global styles (Tailwind + Auth0 theme)
│ ├── react-vendor.[hash].js # React + ReactDOM (~324 kB)
│ ├── vendor.[hash].js # Third-party dependencies (~196 kB)
│ └── common.[hash].js # Shared app code (~87 kB)
│ ├── react-vendor.[hash].js # React core (~194 kB)
│ ├── vendor.[hash].js # Third-party dependencies (~249 kB)
│ └── common.[hash].js # Shared app code (~49 kB)
└── [screen-name]/
└── index.[hash].js # Screen-specific code (0.9-6 kB)
```

**Bundle Strategy:**
- **react-vendor**: React and ReactDOM for optimal caching
- **react-vendor**: React + ReactDOM for optimal caching
- **vendor**: Third-party packages (captcha providers, utilities)
- **common**: Shared components and utilities from src/
- **Screen bundles**: Minimal screen-specific logic for fast loading
Expand Down
6 changes: 3 additions & 3 deletions react-js/scripts/build-local.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,15 @@ try {
html += `<link rel="stylesheet" href="${baseUrl}/${cssFile}">\n`;
});

// Find and order JS files: main, react-vendor, vendor, common, screen-specific
// Find JS files
const mainFile = jsFiles.find(file => file.includes('main.') && !file.includes('shared/'));
const reactVendorFile = jsFiles.find(file => file.includes('shared/react-vendor.'));
const vendorFile = jsFiles.find(file => file.includes('shared/vendor.'));
const commonFile = jsFiles.find(file => file.includes('shared/common.'));
const screenFile = jsFiles.find(file => file.includes(`${screenName}/index.`));

// Add scripts in the correct dependency order
[mainFile, reactVendorFile, vendorFile, commonFile, screenFile].forEach(file => {
// Add all script tags
[reactVendorFile, vendorFile, commonFile, screenFile, mainFile].forEach(file => {
if (file) {
html += `<script src="${baseUrl}/${file}" type="module"></script>\n`;
}
Expand Down
3 changes: 2 additions & 1 deletion react-js/src/components/form/ULThemeFloatingLabelField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ function ULThemeFloatingLabelField({
size = "default",
wrapperClassName,
error,
id,
...props
}: ULThemeFloatingLabelFieldProps) {
// Get the form field context for proper ID association
Expand All @@ -104,7 +105,7 @@ function ULThemeFloatingLabelField({
return (
<div className={cn("w-full", wrapperClassName)}>
<BaseFloatingLabelField
id={formItemId}
id={id || formItemId}
className={cn(className, themeOverrides)}
variant={effectiveVariant}
size={size}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,23 @@ const AlternativeLogins = () => {
locales,
} = useLoginIdManager();
const { texts } = screen;
const { isPasskeyEnabled, alternateConnections } = transaction;
const { isPasskeyEnabled, showPasskeyAutofill, alternateConnections } =
transaction;

const connections = alternateConnections as SocialConnection[] | undefined;

// Handle text fallbacks using locales
const passkeyButtonText =
texts?.passkeyButtonText || locales.form.passkeyButton;

// Only show passkey button if passkeys are enabled AND autofill is NOT active
// When showPasskeyAutofill is true, passkey selection happens via input autocomplete
const showPasskeyButton = isPasskeyEnabled && !showPasskeyAutofill;

return (
<>
<div className="space-y-3 mt-2">
{isPasskeyEnabled && (
{showPasskeyButton && (
<ULThemeSocialProviderButton
key="passkey"
displayName="Passkey"
Expand Down
24 changes: 20 additions & 4 deletions react-js/src/screens/login-id/components/LoginIdForm.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useEffect, useRef } from "react";
import { useForm } from "react-hook-form";

import type { Error, LoginOptions } from "@auth0/auth0-acul-js/types";
Expand Down Expand Up @@ -83,10 +84,24 @@ function LoginIdForm() {
const shouldShowCountryPicker = isPhoneNumberSupported(loginIdentifiers);
const selectedCountry = transformAuth0CountryCode(countryCode, countryPrefix);

// Enable passkey autofill for identifier field if supported
if (transaction?.isPasskeyEnabled) {
loginIdInstance.registerPasskeyAutofill("username");
}
// Track if passkey autofill has been registered to prevent duplicate calls
const passkeyRegistered = useRef(false);

// Enable passkey autofill for identifier field if supported (run once on mount)
useEffect(() => {
if (
transaction?.isPasskeyEnabled &&
transaction?.showPasskeyAutofill &&
!passkeyRegistered.current
) {
passkeyRegistered.current = true;
loginIdInstance.registerPasskeyAutofill("username");
}
}, [
transaction?.isPasskeyEnabled,
transaction?.showPasskeyAutofill,
loginIdInstance,
]);

// Proper submit handler with form data
const onSubmit = async (data: LoginOptions): Promise<void> => {
Expand Down Expand Up @@ -130,6 +145,7 @@ function LoginIdForm() {
<FormItem>
<ULThemeFloatingLabelField
{...field}
id="username"
value={String(field.value || "")}
label={identifierLabel}
type={identifierType}
Expand Down
39 changes: 17 additions & 22 deletions react-js/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,36 +88,31 @@ export default defineConfig({
return "assets/shared/[name].[hash][extname]";
},

// Simplified manual chunks strategy with forced common chunk
manualChunks: (id) => {
// React, React-DOM, and Base UI in a separate chunk (Base UI depends on React)
if (
id.includes("node_modules") &&
(id.includes("react") ||
id.includes("react-dom") ||
id.includes("@base-ui-components"))
) {
return "react-vendor";
}
if (!id.includes("node_modules")) {
const absoluteId = resolve(id);
const absoluteSrcScreensDir = resolve(__dirname, "src/screens");

// All other node_modules dependencies go into vendor chunk
if (id.includes("node_modules")) {
return "vendor";
if (
absoluteId.includes(resolve(__dirname, "src/")) &&
!absoluteId.startsWith(absoluteSrcScreensDir + "/")
) {
return "common";
}
return undefined;
}

// Everything in src/ but NOT in src/screens/ goes to common
const absoluteId = resolve(id);
const absoluteSrcScreensDir = resolve(__dirname, "src/screens");

// React core packages (no external dependencies) go to react-vendor
if (
absoluteId.includes(resolve(__dirname, "src/")) &&
!absoluteId.startsWith(absoluteSrcScreensDir + "/")
id.includes("/node_modules/react/") ||
id.includes("/node_modules/react-dom/") ||
id.includes("/node_modules/scheduler/") // Keep React's internals together
) {
return "common";
return "react-vendor";
}

// For screen-specific code or other cases, let Rollup decide
return undefined;
// All other node_modules go to vendor
return "vendor";
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ for css_file in "${SHARED_CSS_FILES[@]}" "${SCREEN_CSS_FILES[@]}"; do
fi
done

# Add JS files in correct dependency order: main → react-vendor (React + React-DOM + Base UI) → vendor → common → screen-specific
# Add JS files in correct dependency order: react-vendor → vendor → common → screen-specific → main
# First collect files by type
MAIN_JS_FILE=""
REACT_VENDOR_JS_FILE=""
Expand Down Expand Up @@ -71,8 +71,8 @@ for js_file in "${SHARED_JS_FILES[@]}"; do
fi
done

# Add scripts in dependency order: main → react-vendor (React + Base UI) → vendor → common → screen entry
for js_file in "$MAIN_JS_FILE" "$REACT_VENDOR_JS_FILE" "$VENDOR_JS_FILE" "$COMMON_JS_FILE" "$SCREEN_ENTRY_FILE"; do
# Add scripts in dependency order: react-vendor → vendor → common → screen entry → main
for js_file in "$REACT_VENDOR_JS_FILE" "$VENDOR_JS_FILE" "$COMMON_JS_FILE" "$SCREEN_ENTRY_FILE" "$MAIN_JS_FILE"; do
if [ -z "$js_file" ]; then continue; fi
js_basename=$(basename "$js_file")
if [[ ! "$js_basename" =~ ^[^a-zA-Z0-9] ]]; then
Expand Down
8 changes: 4 additions & 4 deletions react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,16 @@ dist/
├── main.[hash].js # Main application bundle
├── shared/
│ ├── style.[hash].css # Global styles (Tailwind + Auth0 theme)
│ ├── react-vendor.[hash].js # React + ReactDOM (~324 kB)
│ ├── vendor.[hash].js # Third-party dependencies (~196 kB)
│ └── common.[hash].js # Shared app code (~87 kB)
│ ├── react-vendor.[hash].js # React core (~194 kB)
│ ├── vendor.[hash].js # Third-party dependencies (~347 kB)
│ └── common.[hash].js # Shared app code (~95 kB)
└── [screen-name]/
└── index.[hash].js # Screen-specific code (0.9-6 kB)
```

**Bundle Strategy:**

- **react-vendor**: React and ReactDOM for optimal caching
- **react-vendor**: React + ReactDOM for optimal caching
- **vendor**: Third-party packages (captcha providers, form libraries, utilities)
- **common**: Shared components, hooks, and utilities from src/
- **Screen bundles**: Minimal screen-specific logic for fast loading
Expand Down
14 changes: 6 additions & 8 deletions react/scripts/build-local.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ try {
html += `<link rel="stylesheet" href="${baseUrl}/${cssFile}">\n`;
});

// Find and order JS files: main, react-vendor, vendor, common, screen-specific
// Find JS files
const mainFile = jsFiles.find(
(file) => file.includes("main.") && !file.includes("shared/")
);
Expand All @@ -129,14 +129,12 @@ try {
file.includes(`${screenName}/index.`)
);

// Add scripts in the correct dependency order
[mainFile, reactVendorFile, vendorFile, commonFile, screenFile].forEach(
(file) => {
if (file) {
html += `<script src="${baseUrl}/${file}" type="module"></script>\n`;
}
// Add all script tags
[reactVendorFile, vendorFile, commonFile, screenFile, mainFile].forEach((file) => {
if (file) {
html += `<script src="${baseUrl}/${file}" type="module"></script>\n`;
}
);
});

return html;
};
Expand Down
7 changes: 6 additions & 1 deletion react/src/screens/login-id/components/AlternativeLogins.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const AlternativeLogins = () => {
texts,
locales,
isPasskeyEnabled,
showPasskeyAutofill,
alternateConnections,
handleFederatedLogin,
handlePasskeyLogin,
Expand All @@ -28,10 +29,14 @@ const AlternativeLogins = () => {
handleFederatedLogin(federatedLoginOptions);
};

// Only show passkey button if passkeys are enabled AND autofill is NOT active
// When showPasskeyAutofill is true, passkey selection happens via input autocomplete
const showPasskeyButton = isPasskeyEnabled && !showPasskeyAutofill;

return (
<>
<div className="space-y-3 mt-2">
{isPasskeyEnabled && (
{showPasskeyButton && (
<ULThemeSocialProviderButton
key="passkey"
displayName={locales?.alternativeLogins?.passkeyLabel}
Expand Down
4 changes: 3 additions & 1 deletion react/src/screens/login-id/components/LoginIdForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ function LoginIdForm() {
resetPasswordLink,
isCaptchaAvailable,
isPasskeyEnabled,
showPasskeyAutofill,
handleLoginId,
handlePickCountryCode,
} = useLoginIdManager();
Expand Down Expand Up @@ -77,7 +78,8 @@ function LoginIdForm() {
);

// Enable passkey autofill for identifier field if supported
if (isPasskeyEnabled) {
// Only register autofill when showPasskeyAutofill is true
if (isPasskeyEnabled && showPasskeyAutofill) {
// eslint-disable-next-line react-hooks/rules-of-hooks
usePasskeyAutofill();
}
Expand Down
2 changes: 2 additions & 0 deletions react/src/screens/login-id/hooks/useLoginIdManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const useLoginIdManager = () => {
isSignupEnabled,
isForgotPasswordEnabled,
isPasskeyEnabled,
showPasskeyAutofill,
countryCode,
countryPrefix,
} = transaction;
Expand Down Expand Up @@ -78,6 +79,7 @@ export const useLoginIdManager = () => {
isSignupEnabled,
isForgotPasswordEnabled,
isPasskeyEnabled,
showPasskeyAutofill,
isCaptchaAvailable,
captcha,
alternateConnections,
Expand Down
38 changes: 17 additions & 21 deletions react/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,35 +88,31 @@ export default defineConfig({
return "assets/shared/[name].[hash][extname]";
},

// Simplified manual chunks strategy with forced common chunk
manualChunks: (id) => {
// React, React-DOM, and Base UI in a separate chunk (Base UI depends on React)
if (
id.includes("node_modules") &&
(id.includes("react") ||
id.includes("react-dom") ||
id.includes("@base-ui-components"))
) {
return "react-vendor";
}
if (!id.includes("node_modules")) {
const absoluteId = resolve(id);
const absoluteSrcScreensDir = resolve(__dirname, "src/screens");

if (id.includes("node_modules")) {
return "vendor";
if (
absoluteId.includes(resolve(__dirname, "src/")) &&
!absoluteId.startsWith(absoluteSrcScreensDir + "/")
) {
return "common";
}
return undefined;
}

// Everything in src/ but NOT in src/screens/ goes to common
const absoluteId = resolve(id);
const absoluteSrcScreensDir = resolve(__dirname, "src/screens");

// React core packages (no external dependencies) go to react-vendor
if (
absoluteId.includes(resolve(__dirname, "src/")) &&
!absoluteId.startsWith(absoluteSrcScreensDir + "/")
id.includes("/node_modules/react/") ||
id.includes("/node_modules/react-dom/") ||
id.includes("/node_modules/scheduler/") // Keep React's internals together
) {
return "common";
return "react-vendor";
}

// For screen-specific code or other cases, let Rollup decide
return undefined;
// All other node_modules go to vendor
return "vendor";
},
},
},
Expand Down
Loading