Q:

How do you rotate a rectangle with points inside?

The situation is as followed: I have a rectangular grid. Which has a dot in it. Located at the top left (position: x:1 & y:1). The coordinates from this grid range from 1 to 4. Using a rotate function like described in this question. I am able to rotate this dot according the center point of a rectangular canvas. For this I use (width + 1) /2 and (height + 1)/2. The addition of the number 1 is related to the canvas, and just creates a white margin/offset/border around the rectangular grid where I plot my coordinates to. The dimensions of this canvas range from 0 to 5.
When rotating along a square grid. Everything goes well. But when the width is not equal to its height. The desired outcome is not as intended. It should rotate as a Tetris block. But the "points" of the Tetris block move outside its grid. 
Below I have visualized this problem in more depth. The red dot is the point which needs to be rotated. The light blue/green point is the center point which the red dots is being rotated. The rotation is 90 degrees clockwise.
Update: Visualisation of problem
What happens in a square grid

What happens in a rectangular grid

Code snippet
function rotate(cx, cy, x, y, angle) {
    var radians = (Math.PI / 180) * -angle,
        cos = Math.cos(radians),
        sin = Math.sin(radians),
        nx = (cos * (x - cx)) + (sin * (y - cy)) + cx,
        ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;
    return {x:nx, y: ny};
}

rotate((width +1)/2, (height + 1)/2, x,y, 90)
var tempWidth = width;
width = height;
height = tempWidth;

What is the correct way to rotate a grid with its contents 90 degrees.
Update2: Visual code snippet

const svgHelper = {
    init: (width, height, scale) => {
        const svg = document.createElementNS("http://www.w3.org/2000/svg", 'svg');
        svg.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink")
        svg.setAttribute("x", "0px")
        svg.setAttribute("y", "0px")
        svg.setAttribute("viewBox", `0 0 ${width + 1} ${height + 1}`)
        svg.setAttribute("xml:space", `preserve`)

        return svg;

    },

    addCircle: (element, color, x, y, radius, className) => {
          element.insertAdjacentHTML("beforeend", `\r<circle 
          style="fill: ${color}"
          cx="${x}"
          cy="${y}"
          r="${radius}"
          class="${className}"
          />`);
          return element.lastChild;
    },
    addText: (element, string) => {
          element.insertAdjacentHTML("beforeend", `\r<text x="0.2" y=".2">${string}</text>`);
          return element.lastChild;
    }
}

const Grid = {
    init: (width, height, margin) => {
        const result = {
            margin: margin,
            array: []
        };
        for (var i = 0; i < height; i++) {
            result.array.push([]);
            for (var ii = 0; ii < width; ii++) {
                result.array[i].push(0);
            }
        }
        return result.array;
    },
    draw: (svg) => {
        const tmp = svg.getAttribute("viewBox").split(" ");
        const width = tmp[2]
        const height = tmp[3]

        for (var y = 1; y < height; y++) {
            for (var x = 1; x < width; x++) {
                var posX = x //grid.margin
                var posY = y //grid.margin

                svgHelper.addCircle(svg, "#eee", posX, posY, .1);
            }
        }
    }
}

const updateGrid = (width, height,element) => {
    element.setAttribute("viewBox", `0 0 ${width + 1} ${height + 1}`)

    // Remove old points
    var points = element.querySelectorAll("circle")
    for (var i = 0; i < points.length; i++) {
        points[i].remove();
    }
    Grid.draw(element);
}

function rotate(cx, cy, x, y, angle) {
    var radians = (Math.PI / 180) * -angle,
        cos = Math.cos(radians),
        sin = Math.sin(radians),
        nx = (cos * (x - cx)) + (sin * (y - cy)) + cx,
        ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;
    return {x:nx, y: ny};
}


const draw = (width, height) => {

    const div = document.createElement("div");
    const element = svgHelper.init(width, height);
    const span = document.createElement("span");
    div.appendChild(element);
    div.appendChild(span);
  
    span.className = "text";
    let point = {x:1, y:1};
    document.querySelector("body").appendChild(div);

    Grid.draw(element);
    let prevPoint = svgHelper.addCircle(element, "#f00", point.x, point.y, .125, "point")

    setInterval(() => {
        
        var tmpWidth = width;
        width = height;
        height = tmpWidth;
        updateGrid(width, height, element)
        
        point = rotate((width + 1)/2, (height + 1)/2, point.x, point.y, 90)
        prevPoint = svgHelper.addCircle(element, "#f00", point.x, point.y, .125, "point")
        span.innerText = `x: ${Math.round(point.x)} y: ${Math.round(point.y)}`;

    }, 1000)    
}
    
draw(4,4)
draw(1,4)
div {
    position: relative;
    display: inline-block;
}

svg {
    width: 135px;
    height: 135px;
    background-color: lightgray;
    margin: 10px;
}

span {
    font-size: 10px;
    display: block;
    margin-left: 10px;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
  <link rel="stylesheet" href="./index.css">
</head>
<body>
<script src="index.js"></script>
</body>
</html>

A:

The problem appears because your formula for point rotation doesn't take into account that the scales in X and Y directions are different. How can we fix it?
The easiest solution (IMHO) should contain three steps:

Map points to the square grid
Apply rotation and calculate new point coordinates
Map points back to the rectangular grid

The mapping function could be easily found from the relationship
x_new = (x_old - x_center) * scale + x_center
x_old = (x_new - x_center) / scale + x_center

The relations for y coordinate whould be pretty the same, so I ommited them.
Thus your rotation function should be tweaked as follows:
function rotate(cx, cy, x, y, angle, scaleX, scaleY) { // <= Adding scale factors here
  // Mapping initial points to a square grid
  x = (x - cx) / scaleX + cx;
  y = (y - cy) / scaleY + cy;

  // Applying rotation
  var radians = (Math.PI / 180) * -angle,
    cos = Math.cos(radians),
    sin = Math.sin(radians),
    nx = (cos * (x - cx)) + (sin * (y - cy)) + cx,
    ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;

  // Mapping new point coordinates back to rectangular grid
  nx = (nx - cx) * scaleX + cx;
  ny = (ny - cy) * scaleY + cy;

  return {x: nx, y: ny};
}

Here is an animation, how it works now:

And here is a working example:

function clearAll(container) {
  const ctx = container.getContext('2d');
  ctx.clearRect(0, 0, 300, 300);
}

function putPoint(container, x, y, r, color) {
  const ctx = container.getContext('2d');
  ctx.beginPath();
  ctx.fillStyle = color;
  ctx.arc(x, y, r, 0, 2 * Math.PI);
  ctx.fill();
}

function drawGrid(container, scaleX, scaleY, elapsedTime) {
  const scale = 60;
  const offset = 60;

  const centerX = offset + 1.5 * scale * scaleX;
  const centerY = offset + 1.5 * scale * scaleY;

  clearAll(container);
  putPoint(container, centerX, centerY, 3, 'cyan');

  for (let i = 0; i < 4; i++) {
    for (let j = 0; j < 4; j++) {
      const x = offset + scaleX * scale * i;
      const y = offset + scaleY * scale * j;
      putPoint(container, x, y, 2, 'black');
    }
  }

  putPoint(container, offset, offset, 5, 'red');
  const newPosition1 = rotate(centerX, centerY, offset, offset, 90, scaleX, scaleY);
  putPoint(container, newPosition1.x, newPosition1.y, 5, 'red');

  const newPosition2 = rotate(centerX, centerY, offset, offset, 180, scaleX, scaleY);
  putPoint(container, newPosition2.x, newPosition2.y, 5, 'red');

  const newPosition3 = rotate(centerX, centerY, offset, offset, 270, scaleX, scaleY);
  putPoint(container, newPosition3.x, newPosition3.y, 5, 'red');

  const newPosition4 = rotate(centerX, centerY, offset, offset, 45, scaleX, scaleY);
  putPoint(container, newPosition4.x, newPosition4.y, 5, 'red');

  const newPositionDynamic = rotate(centerX, centerY, offset, offset, elapsedTime, scaleX, scaleY);
  putPoint(container, newPositionDynamic.x, newPositionDynamic.y, 5, 'red');


  requestAnimationFrame(() => {
    drawGrid(container, scaleX, scaleY, elapsedTime + 1);
  })
}

function rotate(cx, cy, x, y, angle, scaleX, scaleY) { // <= Adding scale factors here
  // Mapping initial points to a square grid
  x = (x - cx) / scaleX + cx;
  y = (y - cy) / scaleY + cy;

  // Applying rotation
  var radians = (Math.PI / 180) * -angle,
cos = Math.cos(radians),
sin = Math.sin(radians),
nx = (cos * (x - cx)) + (sin * (y - cy)) + cx,
ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;

  // Mapping new point coordinates back to rectangular grid
  nx = (nx - cx) * scaleX + cx;
  ny = (ny - cy) * scaleY + cy;

  return {x: nx, y: ny};
}

const squareGrid = document.getElementById('square-grid');
const rectangularGrid = document.getElementById('rectangular-grid');

drawGrid(squareGrid, 1, 1, 0);
drawGrid(rectangularGrid, 0.5, 0.9, 0);
canvas {
  width: 300px;
  height: 300px;
  background-color: lightgray;
  margin: 10px;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
  <link rel="stylesheet" href="./index.css">
</head>
<body>
<canvas width="300" height="300" id="square-grid"></canvas>
<canvas width="300" height="300" id="rectangular-grid"></canvas>

<script src="index.js"></script>
</body>
</html>

Please, let me know if I understood you wrong or this solution is not the one you expected to get

