Repository: microsoft/griffel
Pull request #202 (2022-08-31 13:23:03)

fix: do not emit sequence hashes for empty sets
There are cases when a style set is [empty intentionally](https://github.com/microsoft/fluentui/blob/21af8da1d98c070a682b26b7d56775d43a749b3d/packages/react-components/react-button/src/components/Button/useButtonStyles.ts#L122-L124):

```ts
makeStyles({
  root: { /* no rules */ },
})
```

This PR fixes the implementation to not emit a class in such cases to avoid it in DOM & useless class merging if it's passed to `mergeClasses()`.


Patch:
diff --git a/change/@griffel-core-7e21a596-c13f-4428-a875-d535f075b2c3.json b/change/@griffel-core-7e21a596-c13f-4428-a875-d535f075b2c3.json
new file mode 100644
index 0000000..eb870c1
--- /dev/null
+++ b/change/@griffel-core-7e21a596-c13f-4428-a875-d535f075b2c3.json
@@ -0,0 +1,7 @@
+{
+  "type": "patch",
+  "comment": "fix: do not emit sequence hashes for empty sets",
+  "packageName": "@griffel/core",
+  "email": "olfedias@microsoft.com",
+  "dependentChangeType": "patch"
+}
diff --git a/change/@griffel-devtools-32883921-647e-4e71-8659-54152995e3e5.json b/change/@griffel-devtools-32883921-647e-4e71-8659-54152995e3e5.json
new file mode 100644
index 0000000..5bdb7af
--- /dev/null
+++ b/change/@griffel-devtools-32883921-647e-4e71-8659-54152995e3e5.json
@@ -0,0 +1,7 @@
+{
+  "type": "patch",
+  "comment": "fix devtools to resolve relative source url",
+  "packageName": "@griffel/devtools",
+  "email": "yuanboxue@microsoft.com",
+  "dependentChangeType": "patch"
+}
diff --git a/packages/core/src/runtime/reduceToClassNameForSlots.ts b/packages/core/src/runtime/reduceToClassNameForSlots.ts
index 270e7c8..d05b12e 100644
--- a/packages/core/src/runtime/reduceToClassNameForSlots.ts
+++ b/packages/core/src/runtime/reduceToClassNameForSlots.ts
@@ -42,10 +42,16 @@ export function reduceToClassNameForSlots<Slots extends string | number>(
 
   // eslint-disable-next-line guard-for-in
   for (const slotName in classesMapBySlot) {
-    const classnamesForSlot = reduceToClassName(classesMapBySlot[slotName], dir);
+    const slotClasses = reduceToClassName(classesMapBySlot[slotName], dir);
 
-    const sequenceHash = hashSequence(classnamesForSlot, dir);
-    const resultSlotClasses = sequenceHash + ' ' + classnamesForSlot;
+    // Handles a case when there are no classes in a set i.e. "makeStyles({ root: {} })"
+    if (slotClasses === '') {
+      classNamesForSlots[slotName] = '';
+      continue;
+    }
+
+    const sequenceHash = hashSequence(slotClasses, dir);
+    const resultSlotClasses = sequenceHash + ' ' + slotClasses;
 
     DEFINITION_LOOKUP_TABLE[sequenceHash] = [classesMapBySlot[slotName], dir];
     classNamesForSlots[slotName] = resultSlotClasses;
diff --git a/packages/devtools/src/sourceMap/loadOriginalSourceLoc.ts b/packages/devtools/src/sourceMap/loadOriginalSourceLoc.ts
index e4e8095..2d08f47 100644
--- a/packages/devtools/src/sourceMap/loadOriginalSourceLoc.ts
+++ b/packages/devtools/src/sourceMap/loadOriginalSourceLoc.ts
@@ -33,7 +33,19 @@ function parseSourceUrl(sourceUrlWithLoc: string): MappedPosition | null {
   const paths = sourceUrlWithLoc.split(':');
   const column = Number(paths.pop());
   const line = Number(paths.pop());
-  const source = paths.join(':');
+
+  let source = paths.join(':');
+  try {
+    // url can contain relative path when webpack is used; normalize it
+    const { protocol, pathname } = new URL(source);
+    if (!protocol.startsWith('http')) {
+      const absPathname = new URL(`http://${pathname}`).pathname; // URL only normalizes pathname for http/https
+      source = `${protocol}//${absPathname}`;
+    }
+  } catch (error) {
+    console.warn(error);
+  }
+
   if (Number.isNaN(line) || Number.isNaN(column)) {
     return null;
   }


Test patch:
diff --git a/packages/core/src/makeStyles.test.ts b/packages/core/src/makeStyles.test.ts
index 4064348..db274d0 100644
--- a/packages/core/src/makeStyles.test.ts
+++ b/packages/core/src/makeStyles.test.ts
@@ -17,6 +17,13 @@ describe('makeStyles', () => {
     document.head.innerHTML = '';
   });
 
+  it('returns an empty classname for an empty style set', () => {
+    const computeClasses = makeStyles({
+      root: {},
+    });
+    expect(computeClasses({ dir: 'ltr', renderer }).root).toEqual('');
+  });
+
   it('returns a single classname for a single style', () => {
     const computeClasses = makeStyles({
       root: {