Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Permit custom identifier hashing #657

Closed
Closed
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
5 changes: 5 additions & 0 deletions .changeset/rude-carrots-peel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@vanilla-extract/css': minor
---

Users can now provide a custom identifier hashing function
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ Different formatting of identifiers (e.g. class names, keyframes, CSS Vars, etc)

- `short` identifiers are a 7+ character hash. e.g. `hnw5tz3`
- `debug` identifiers contain human readable prefixes representing the owning filename and a potential rule level debug name. e.g. `myfile_mystyle_hnw5tz3`
- A function accepting three parameters: A scope name, an index, and an optional rule level debug name, and returning the identifier.

Each integration will set a default value based on the configuration options passed to the bundler.

Expand Down
29 changes: 29 additions & 0 deletions packages/css/src/identifier.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { removeAdapter, setAdapter } from './adapter';
import { setFileScope, endFileScope } from './fileScope';
import { generateIdentifier } from './identifier';

Expand Down Expand Up @@ -25,4 +26,32 @@ describe('identifier', () => {
`"debug_and_more__skkcyc2"`,
);
});

describe('with custom callback', () => {
beforeAll(() => {
setAdapter({
appendCss: () => {},
registerClassName: () => {},
onEndFileScope: () => {},
registerComposition: () => {},
markCompositionUsed: () => {},
getIdentOption: () => (scope, index, dbg) =>
`abc_${dbg}_${scope}_${index}`,
});
});

afterAll(() => {
removeAdapter();
});

it('defers to a custom callback', () => {
expect(generateIdentifier(`a`)).toMatchInlineSnapshot(`"abc_a_test_3"`);
});

it('rejects invalid identifiers', () => {
// getIdentOption() does not remove spaces from the debug info so the
// resulting identifier should be invalid here.
expect(() => generateIdentifier(`a b`)).toThrow();
});
});
});
46 changes: 34 additions & 12 deletions packages/css/src/identifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,46 @@ function getDevPrefix(debugId: string | undefined) {
return parts.join('_');
}

export function generateIdentifier(debugId: string | undefined) {
function generateShortIdentifier(scope: string, index: number) {
// Convert ref count to base 36 for optimal hash lengths
const refCount = getAndIncrementRefCounter().toString(36);
const { filePath, packageName } = getFileScope();
const refCountStr = index.toString(36);
const fileScopeHash = hash(scope);
return `${fileScopeHash}${refCountStr}`;
}

const fileScopeHash = hash(
packageName ? `${packageName}${filePath}` : filePath,
);
function generateDebugIdentifier(
scope: string,
index: number,
debugId: string | undefined,
) {
let identifier = generateShortIdentifier(scope, index);
const devPrefix = getDevPrefix(debugId);
if (devPrefix) {
identifier = `${devPrefix}__${identifier}`;
}
return identifier;
}

let identifier = `${fileScopeHash}${refCount}`;
export function generateIdentifier(debugId: string | undefined) {
const refCount = getAndIncrementRefCounter();
const { filePath, packageName } = getFileScope();
const fileScopeStr = packageName ? `${packageName}${filePath}` : filePath;
const opt = getIdentOption();

if (getIdentOption() === 'debug') {
const devPrefix = getDevPrefix(debugId);
let identifier: string;
if (opt === 'short') {
identifier = generateShortIdentifier(fileScopeStr, refCount);
identifier = identifier.match(/^[0-9]/) ? `_${identifier}` : identifier;
} else if (opt === 'debug') {
identifier = generateDebugIdentifier(fileScopeStr, refCount, debugId);
identifier = identifier.match(/^[0-9]/) ? `_${identifier}` : identifier;
} else {
identifier = opt(fileScopeStr, refCount, debugId);

if (devPrefix) {
identifier = `${devPrefix}__${identifier}`;
if (!identifier.match(/^[A-Z_][0-9A-Z_]+$/i)) {
throw new Error(`Identifier function returned invalid indentifier: "${identifier}"`);
}
}

return identifier.match(/^[0-9]/) ? `_${identifier}` : identifier;
FrancoisChabot marked this conversation as resolved.
Show resolved Hide resolved
return identifier;
}
6 changes: 5 additions & 1 deletion packages/css/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,11 @@ export interface Composition {
classList: string;
}

type IdentOption = 'short' | 'debug';
type IdentOption =
| 'short'
| 'debug'
| ((scope: string, index: number, debugId: string | undefined) => string);

export interface Adapter {
appendCss: (css: CSS, fileScope: FileScope) => void;
registerClassName: (className: string) => void;
Expand Down