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
4 changes: 4 additions & 0 deletions packages/generator-widget/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [Unreleased]

### Changed

- We now enforce validation when choosing an organization name for a widget.

## [10.24.0] - 2025-09-24

### Changed
Expand Down
9 changes: 9 additions & 0 deletions packages/generator-widget/generators/app/lib/prompttexts.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ function promptWidgetProperties(mxProjectDir, widgetName) {
{
type: "input",
name: "organization",
validate: input => {
if (/[^a-zA-Z0-9_.-]/.test(input)) {
return "Your organization name can only contain alphanumeric characters, '_', '-', and '.'. Please provide a valid name";
}
if (!/^([a-zA-Z0-9_-]+.)*[a-zA-Z0-9_-]+$/.test(input)) {
return "Your organization name must follow the structure [namespace.]org-name, for example 'mendix' or 'com.mendix.widgets'. Please provide a valid name";
}
return true;
},
message: "Organization name",
default: "Mendix",
store: true
Expand Down
2 changes: 1 addition & 1 deletion packages/generator-widget/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mendix/generator-widget",
"version": "10.24.0",
"version": "11.3.1",
"description": "Mendix Pluggable Widgets Generator",
"engines": {
"node": ">=16"
Expand Down
2 changes: 2 additions & 0 deletions packages/pluggable-widgets-tools/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

- We fixed an issue where `require` was not transformed to `import` for the `es` output format which could result in an error when the widget was used in a project with React client enabled.

- We now enforce the same validation for the `widgetName` in the widget bundler as we do in the generator. Validation is now also enforced for the organization name (`packagePath`).

## [11.3.0] - 2025-11-12

### Changed
Expand Down
5 changes: 5 additions & 0 deletions packages/pluggable-widgets-tools/configs/shared.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { existsSync, readdirSync, promises as fs, readFileSync } from "node:fs";
import { join, relative } from "node:path";
import { config } from "dotenv";
import colors from "ansi-colors";
import { throwOnIllegalChars, throwOnNoMatch } from "../dist/utils/validation.js";

config({ path: join(process.cwd(), ".env") });

Expand All @@ -25,6 +26,10 @@ if (!widgetName || !widgetPackageJson) {
throw new Error("Widget does not define widgetName in its package.json");
}

throwOnIllegalChars(widgetName, "a-zA-Z", "The `widgetName` property in package.json")
throwOnIllegalChars(widgetPackage, "a-zA-Z0-9_.-", "The `packagePath` property in package.json")
throwOnNoMatch(widgetPackage, /^([a-zA-Z0-9_-]+.)*[a-zA-Z0-9_-]+$/, "The `packagePath` property in package.json")

const widgetSrcFiles = readdirSync(join(sourcePath, "src")).map(file => join(sourcePath, "src", file));
export const widgetEntry = widgetSrcFiles.filter(file =>
file.match(new RegExp(`[/\\\\]${escape(widgetName)}\\.[jt]sx?$`, "i"))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { WidgetValidationError, throwOnIllegalChars, throwOnNoMatch } from "../validation"

describe("Validation Utilities", () => {

describe("throwOnIllegalChars", () => {

it("throws when the input does not match the pattern", () => {
expect(throwOnIllegalChars.bind(null, "abc", '0-9', "Test")).toThrow(WidgetValidationError)
})

it("does not throw when the input does match the pattern", () => {
expect(throwOnIllegalChars.bind(null, "abc", 'a-z', "Test")).not.toThrow()
})
})

describe("throwOnNoMatch", () => {

it("throws when the input does not match the pattern", () => {
expect(throwOnNoMatch.bind(null, "abc", /^$/, "Test")).toThrow(WidgetValidationError)
})

it("does not throw when the input does match the pattern", () => {
expect(throwOnNoMatch.bind(null, "abc", /[a-z]/, "Test")).not.toThrow()
})
})

})

47 changes: 47 additions & 0 deletions packages/pluggable-widgets-tools/src/utils/validation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@

export class WidgetValidationError extends Error {
name = "Widget Validation Error"

constructor(message: string) {
super(message)
}
}

/**
* Asserts that the given input string only contains the characters specified by `legalCharacters`.
* @param input The string under test
* @param legalCharacters The characters that the input string may contain. Follows character class notation from regex (inside the [])
* @param message The message that is included in the error. Specify where the input is used and can be corrected.
* @throws WidgetValidationError If the input contains characters not allowed by legalCharacters
*/
export function throwOnIllegalChars(input: string, legalCharacters: string, message: string): void {
const pattern = new RegExp(`([^${legalCharacters}])`, "g")
const illegalChars = input.match(pattern);

if (illegalChars === null) {
return
}

const formatted = illegalChars
.reduce((unique, c) => unique.includes(c) ? unique : [...unique, c], [] as string[])
.map(c => `"${c}"`)
.join(", ");

throw new WidgetValidationError(`${message} contains illegal characters ${formatted}. Allowed characters are [${legalCharacters}].`)
}

/**
* Asserts that the given input string matches the given pattern.
* @param input The string under test
* @param pattern The pattern the input must match
* @param message The message that is included in the error. Specify where the input is used and can be corrected.
* @throws WidgetValidationError If the input contains characters not allowed by legalCharacters
*/
export function throwOnNoMatch(input: string, pattern: RegExp, message: string) {
if (pattern.test(input)) {
return
}

throw new WidgetValidationError(`${message} does not match the pattern ${pattern}.`)
}

Loading