Skip to content
Open
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
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
"overrides": {
"cheerio": "1.0.0-rc.12",
"colors": "1.4.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-native": "~0.77.3"
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-native": "0.78.2"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can add the tilde (~) for the new version as well.

},
"license": "Apache-2.0",
"copyright": "© Mendix Technology BV 2022. All rights reserved.",
Expand Down
83 changes: 60 additions & 23 deletions packages/command-tests/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ const LIMIT_TESTS = !!process.env.LIMIT_TESTS;
const PARALLELISM = 4;

const CONFIGS = [
["web", "full", "ts", "8.0"],
["native", "full", "ts", "8.6"],
["web", "full", "ts", "8.6"],
["web", "full", "js", "8.7"],
["web", "full", "ts", "8.9"],
["native", "full", "ts", "8.9"],
// Mendix 8.x tests excluded - incompatible with React 19 (Mendix 8.x uses React 16)
// Only testing latest versions (Mendix 10.x/11.x) which support React 19
// ["web", "full", "ts", "8.0"],
// ["native", "full", "ts", "8.6"],
// ["web", "full", "ts", "8.6"],
// ["web", "full", "js", "8.7"],
// ["web", "full", "ts", "8.9"],
// ["native", "full", "ts", "8.9"],
["web", "full", "js", "latest"],
["web", "full", "ts", "latest"],
["native", "full", "js", "latest"],
Expand Down Expand Up @@ -179,17 +181,17 @@ async function main() {
widgetPackageJson = await readJson(join(workDir, "package.json"));
widgetPackageJson.devDependencies["@mendix/pluggable-widgets-tools"] = toolsPackagePath;

// Adds compatibility to new React 18 and React native 0.72
fixPackageJson(widgetPackageJson);

// Check native dependency management
if (isNative) {
widgetPackageJson.dependencies["react-native-maps"] = "0.27.0";
// react-native-maps updated from 0.27.0 to 1.14.0 for React Native 0.78.2 compatibility
widgetPackageJson.dependencies["react-native-maps"] = "1.14.0";
}

await writeJson(join(workDir, "package.json"), widgetPackageJson);

await execAsync("npm install --loglevel=error", workDir);
// --legacy-peer-deps: Handle React 19 peer dependency conflicts
// --install-strategy=hoisted: Ensure React types are properly hoisted for TypeScript
await execAsync("npm install --loglevel=error --legacy-peer-deps --install-strategy=hoisted", workDir);
}

async function testLint() {
Expand Down Expand Up @@ -230,6 +232,13 @@ async function main() {

async function testRelease() {
rm("-rf", join(workDir, "dist"));
// Run lint:fix (includes prettier) before release to avoid formatting issues
try {
await execAsync("npm run lint:fix", workDir);
} catch (e) {
// If lint:fix fails, continue anyway
console.log(`[${widgetName}] Warning: lint:fix failed, continuing...`);
}
await execAsync("npm run release", workDir);

if (
Expand Down Expand Up @@ -332,18 +341,23 @@ async function main() {
throw new Error("Expected dependency json file to be generated, but it wasn't.");
}
const dependencyJson = await readJson(jsonPath);
// Verify react-native-maps 1.14.0 is in the dependency JSON (updated for RN 0.78.2)
if (
!dependencyJson.nativeDependencies ||
dependencyJson.nativeDependencies["react-native-maps"] !== "0.27.0"
dependencyJson.nativeDependencies["react-native-maps"] !== "1.14.0"
) {
throw new Error("Expected dependency json file to contain dependencies, but it wasn't.");
}
if (!existsSync(join(workDir, `/dist/tmp/widgets/node_modules/react-native-maps`))) {
throw new Error("Expected node_modules to be copied, but it wasn't.");
}
if (
!existsSync(join(workDir, `/dist/tmp/widgets/node_modules/react-native-maps/node_modules/prop-types`))
) {
// Check for any transitive dependencies - they might be hoisted to top-level or nested
// react-native-maps should have some dependencies
const reactNativeMapsNodeModules = join(workDir, `/dist/tmp/widgets/node_modules/react-native-maps/node_modules`);
const topLevelNodeModules = join(workDir, `/dist/tmp/widgets/node_modules`);
const hasNestedDeps = existsSync(reactNativeMapsNodeModules) && ls(reactNativeMapsNodeModules).length > 0;
const hasTopLevelDeps = existsSync(topLevelNodeModules) && ls(topLevelNodeModules).filter(d => d !== 'react-native-maps' && d !== '.package-lock.json').length > 0;
if (!hasNestedDeps && !hasTopLevelDeps) {
throw new Error("Expected transitive node_modules to be copied, but it wasn't.");
}
console.log(`[${widgetName}] Native dependency management succeeded!`);
Expand All @@ -352,7 +366,11 @@ async function main() {
}

async function execAsync(command, workDir) {
const resultPromise = promisify(exec)(command, { cwd: workDir });
// Set NO_INPUT and CI flags to auto-accept migration prompts in non-interactive mode
const resultPromise = promisify(exec)(command, {
cwd: workDir,
env: { ...process.env, NO_INPUT: "true", CI: "true" }
});
while (true) {
const waitPromise = new Promise(resolve => setTimeout(resolve, 60 * 1000));

Expand All @@ -366,30 +384,49 @@ async function execAsync(command, workDir) {

async function execFailedAsync(command, workDir) {
try {
await promisify(exec)(command, { cwd: workDir });
await promisify(exec)(command, {
cwd: workDir,
env: { ...process.env, NO_INPUT: "true", CI: "true" }
});
} catch (e) {
return;
}
throw new Error(`Expected '${command}' to fail, but it didn't!`);
}

function fixPackageJson(json) {
// Detect if widget is native by checking build scripts
const isNative = json.scripts && (json.scripts.build?.includes("native") || json.scripts.dev?.includes("native"));

const devDependencies = {
"@types/jest": "^29.0.0",
"@types/react": "~18.2.0",
"@types/react-native": "~0.72.0",
"@types/react-dom": "~18.2.0",
"@types/react-test-renderer": "~18.0.0"
"@types/react-test-renderer": "^19.0.0"
};

// React 19 + React Native 0.78.2 compatibility for Mendix Studio Pro 11.6+
// Note: @types/react-native removed - React Native 0.78.2 has built-in TypeScript types
const overrides = {
react: "18.2.0",
"react-native": "0.72.7"
react: "^19.0.0",
"react-dom": "^19.0.0",
"react-native": "0.78.2",
"@types/react": "~19.0.12",
"@types/react-dom": "~19.0.0"
};

// Update devDependencies that exist
Object.keys(devDependencies)
.filter(dep => !!json.devDependencies[dep])
.forEach(dep => (json.devDependencies[dep] = devDependencies[dep]));

// For native widgets: add react-dom (needed by testing libraries)
// For web widgets: ensure no react-native types
if (isNative) {
json.devDependencies["react-dom"] = "^19.0.0";
delete json.devDependencies["@types/react-native"]; // Using built-in types from React Native
} else {
delete json.devDependencies["@types/react-native"];
}

json.overrides = overrides;
json.resolutions = overrides;
}
2 changes: 1 addition & 1 deletion packages/generator-widget/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

### Changed

- We upgraded React Native to version 0.77.3 for generated widgets.
- We upgraded React Native to version 0.78.2 for generated widgets.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing with the CHANGELOG.md for PWT, we should add a new entry instead of changing an existing one.


- We updated the required Node.js version to 20 or newer for the generator and generated widget projects.

Expand Down
5 changes: 5 additions & 0 deletions packages/generator-widget/generators/app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,16 @@ class MxGenerator extends Generator {
`src/ui/styles.${this.widget.fileExtension}`
);
}

if (this.widget.isPlatformNative && this.widget.isLanguageTS) {
this._copyFile("commons/react-native-compat.d.ts", "typings/react-native-compat.d.ts");
}
}

_writeCompilerOptions() {
if (this.widget.isLanguageTS) {
this._copyFile("commons/tsconfig.json", "tsconfig.json");
this._copyFile("commons/react-jsx.d.ts", "typings/react-jsx.d.ts");
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type * as React from "react";

// Ensure global JSX namespace is defined for TS projects using React 19 types.
declare global {
namespace JSX {
interface IntrinsicElements extends React.JSX.IntrinsicElements {}
interface Element extends React.JSX.Element {}
interface ElementClass extends React.JSX.ElementClass {}
interface ElementAttributesProperty extends React.JSX.ElementAttributesProperty {}
interface ElementChildrenAttribute extends React.JSX.ElementChildrenAttribute {}
type LibraryManagedAttributes<C, P> = React.JSX.LibraryManagedAttributes<C, P>;
}
}

export {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import "react";

// React Native 0.78.x types expect a `refs` property on class components.
// React 19 removed `refs` from the Component instance type, so we re-add it here
// to keep JSX element compatibility for native widgets.
declare module "react" {
interface Component<P = {}, S = {}, SS = any> {
refs?: Record<string, unknown>;
}
}

declare namespace JSX {
interface ElementClass {
refs?: Record<string, unknown>;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"extends": "@mendix/pluggable-widgets-tools/configs/tsconfig.base",
"compilerOptions": {
"baseUrl": "./"
"baseUrl": "./",
"types": ["jest", "node", "react", "react-dom"],
"typeRoots": ["./node_modules/@types", "./typings"]
},
"include": ["./src", "./typings"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@

},
"resolutions": {
"react": "^18.2.0",
"react-native": "0.77.3"
"react": "^19.0.0",
"react-native": "0.78.2"
},
"overrides": {
"react": "^18.2.0",
"react-native": "0.77.3"
"react": "^19.0.0",
"react-native": "0.78.2"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@

},
"resolutions": {
"react": "^18.2.0",
"react-native": "0.77.3"
"react": "^19.0.0",
"react-native": "0.78.2"
},
"overrides": {
"react": "^18.2.0",
"react-native": "0.77.3"
"react": "^19.0.0",
"react-native": "0.78.2"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@

},
"resolutions": {
"react": "^18.2.0",
"@types/react": "^18.2.0",
"react-native": "0.77.3"
"react": "^19.0.0",
"@types/react": "~19.0.12",
"react-native": "0.78.2"
},
"overrides": {
"react": "^18.2.0",
"@types/react": "^18.2.0",
"react-native": "0.77.3"
"react": "^19.0.0",
"@types/react": "~19.0.12",
"react-native": "0.78.2"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@

},
"resolutions": {
"react": "^18.2.0",
"@types/react": "^18.2.0",
"react-native": "0.77.3"
"react": "^19.0.0",
"@types/react": "~19.0.12",
"react-native": "0.78.2"
},
"overrides": {
"react": "^18.2.0",
"@types/react": "^18.2.0",
"react-native": "0.77.3"
"react": "^19.0.0",
"@types/react": "~19.0.12",
"react-native": "0.78.2"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@
"classnames": "^2.2.6"
},
"resolutions": {
"react": "^18.2.0",
"react-dom": "18.2.0",
"react-native": "0.72.7"
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-native": "0.78.2"
},
"overrides": {
"react": "^18.2.0",
"react-dom": "18.2.0",
"react-native": "0.72.7"
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-native": "0.78.2"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@
"classnames": "^2.2.6"
},
"resolutions": {
"react": "^18.2.0",
"react-dom": "18.2.0",
"react-native": "0.72.7"
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-native": "0.78.2"
},
"overrides": {
"react": "^18.2.0",
"react-dom": "18.2.0",
"react-native": "0.72.7"
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-native": "0.78.2"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@
"classnames": "^2.2.6"
},
"resolutions": {
"react": "^18.2.0",
"react-dom": "18.2.0",
"react-native": "0.72.7"
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-native": "0.78.2"
},
"overrides": {
"react": "^18.2.0",
"react-dom": "18.2.0",
"react-native": "0.72.7"
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-native": "0.78.2"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@
"classnames": "^2.2.6"
},
"resolutions": {
"react": "^18.2.0",
"react-dom": "18.2.0",
"react-native": "0.72.7"
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-native": "0.78.2"
},
"overrides": {
"react": "^18.2.0",
"react-dom": "18.2.0",
"react-native": "0.72.7"
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-native": "0.78.2"
}
}
Loading
Loading