From 7388c5bbee9a29886f52963e210b5ff5e81f13dd Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Tue, 14 Jun 2022 12:31:09 +0200 Subject: [PATCH] unique-id: Adjust `uniqueId()` implementation to only generate valid selectors Regular UUIDs are allowed to start with numeric digits, but CSS selectors may not start with those. This change adjusts the implementation to only return UUIDs starting with a letter instead of a digit. --- .../glimmer/lib/helpers/unique-id.ts | 8 ++++--- .../integration/helpers/unique-id-test.js | 22 +++++++++++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/packages/@ember/-internals/glimmer/lib/helpers/unique-id.ts b/packages/@ember/-internals/glimmer/lib/helpers/unique-id.ts index 831926c75af..bf503bec73c 100644 --- a/packages/@ember/-internals/glimmer/lib/helpers/unique-id.ts +++ b/packages/@ember/-internals/glimmer/lib/helpers/unique-id.ts @@ -37,12 +37,14 @@ export default internalHelper((): Reference => { // This code should be reasonably fast, and provide a unique value every time // it's called, which is what we need here. It produces a string formatted as a // standard UUID, which avoids accidentally turning Ember-specific -// implementation details into an intimate API. +// implementation details into an intimate API. It also ensures that the UUID +// always starts with a letter, to avoid creating invalid IDs with a numeric +// digit at the start. function uniqueId() { // @ts-expect-error this one-liner abuses weird JavaScript semantics that // TypeScript (legitimately) doesn't like, but they're nonetheless valid and // specced. - return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (a) => - (a ^ ((Math.random() * 16) >> (a / 4))).toString(16) + return ([3e7] + -1e3 + -4e3 + -2e3 + -1e11).replace(/[0-3]/g, (a) => + ((a * 4) ^ ((Math.random() * 16) >> (a & 2))).toString(16) ); } diff --git a/packages/@ember/-internals/glimmer/tests/integration/helpers/unique-id-test.js b/packages/@ember/-internals/glimmer/tests/integration/helpers/unique-id-test.js index 763722f49ed..2ae7e8bee1c 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/helpers/unique-id-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/helpers/unique-id-test.js @@ -1,6 +1,6 @@ import { RenderingTestCase, strip, moduleFor, runTask } from 'internal-test-helpers'; -import { setProperties } from '@ember/-internals/metal'; -import { EMBER_UNIQUE_ID_HELPER } from '@ember/canary-features'; +import {setProperties} from '@ember/-internals/metal'; +import {EMBER_UNIQUE_ID_HELPER} from '@ember/canary-features'; if (EMBER_UNIQUE_ID_HELPER) { moduleFor( @@ -102,6 +102,24 @@ if (EMBER_UNIQUE_ID_HELPER) { }); } + ['@test it only generates valid selectors']() { + let iterations = 1000; + let reNumericStart = /^\d/; + + let template = '

{{unique-id}}

'.repeat(iterations); + super.render(template); + + for (let i = 0; i < iterations; i++) { + let textNode = this.nthChild(i).firstChild; + let text = textNode.data; + + this.assert.false( + reNumericStart.test(text), + `{{unique-id}} should produce valid selectors` + text + ); + } + } + render(template, ...rest) { // If there are three parameters to `render`, the second parameter is the // template's arguments.