From e391baec32463c60503f631ace578a71952f8180 Mon Sep 17 00:00:00 2001 From: SombreroElGringo Date: Mon, 21 Aug 2023 20:57:27 +0900 Subject: [PATCH] Allow custom identifier hashing (#1160) --- .changeset/curly-lies-flash.md | 10 +++++++ packages/css/src/identifier.test.ts | 42 ++++++++++++++++++++++++++++- packages/css/src/identifier.ts | 27 +++++++++++++++++-- packages/css/src/types.ts | 14 +++++++--- site/docs/integrations/esbuild.md | 7 +++++ site/docs/integrations/next.md | 7 +++++ site/docs/integrations/rollup.md | 7 +++++ site/docs/integrations/vite.md | 7 +++++ site/docs/integrations/webpack.md | 7 +++++ 9 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 .changeset/curly-lies-flash.md diff --git a/.changeset/curly-lies-flash.md b/.changeset/curly-lies-flash.md new file mode 100644 index 000000000..952019a4c --- /dev/null +++ b/.changeset/curly-lies-flash.md @@ -0,0 +1,10 @@ +--- +'@vanilla-extract/css': minor +'@vanilla-extract/webpack-plugin': minor +'@vanilla-extract/esbuild-plugin': minor +'@vanilla-extract/rollup-plugin': minor +'@vanilla-extract/vite-plugin': minor +'@vanilla-extract/next-plugin': minor +--- + +Users can now provide a custom identifier hashing function diff --git a/packages/css/src/identifier.test.ts b/packages/css/src/identifier.test.ts index 3ef01d6ef..88a6e889a 100644 --- a/packages/css/src/identifier.test.ts +++ b/packages/css/src/identifier.test.ts @@ -1,4 +1,5 @@ -import { setFileScope, endFileScope } from './fileScope'; +import { removeAdapter, setAdapter } from './adapter'; +import { endFileScope, setFileScope } from './fileScope'; import { generateIdentifier } from './identifier'; describe('identifier', () => { @@ -57,4 +58,43 @@ describe('identifier', () => { ).toMatchInlineSnapshot(`"_18bazsm6"`); }); }); + + describe('with custom callback', () => { + beforeAll(() => { + setFileScope('path/to/file.css.ts', 'packagetest'); + setAdapter({ + appendCss: () => {}, + registerClassName: () => {}, + onEndFileScope: () => {}, + registerComposition: () => {}, + markCompositionUsed: () => {}, + getIdentOption: + () => + ({ hash, debugId, filePath, packageName }) => { + const filenameWithExtension = filePath?.split('/').pop(); + const filenameWithoutExtension = + filenameWithExtension?.split('.')?.[0]; + + return `abc_${debugId}_${hash}_${packageName}_${filenameWithoutExtension}`; + }, + }); + }); + + afterAll(() => { + removeAdapter(); + endFileScope(); + }); + + it('defers to a custom callback', () => { + expect(generateIdentifier(`a`)).toMatchInlineSnapshot( + `"abc_a_s0xkdr0_packagetest_file"`, + ); + }); + + 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(); + }); + }); }); diff --git a/packages/css/src/identifier.ts b/packages/css/src/identifier.ts index 5dfe5b1ad..175f6a04b 100644 --- a/packages/css/src/identifier.ts +++ b/packages/css/src/identifier.ts @@ -28,6 +28,10 @@ function getDevPrefix({ return parts.join('_'); } +function normalizeIdentifier(identifier: string) { + return identifier.match(/^[0-9]/) ? `_${identifier}` : identifier; +} + interface GenerateIdentifierOptions { debugId?: string; debugFileName?: boolean; @@ -38,6 +42,7 @@ export function generateIdentifier(options?: GenerateIdentifierOptions): string; export function generateIdentifier( arg?: string | GenerateIdentifierOptions, ): string { + const identOption = getIdentOption(); const { debugId, debugFileName = true } = { ...(typeof arg === 'string' ? { debugId: arg } : null), ...(typeof arg === 'object' ? arg : null), @@ -53,13 +58,31 @@ export function generateIdentifier( let identifier = `${fileScopeHash}${refCount}`; - if (getIdentOption() === 'debug') { + if (identOption === 'debug') { const devPrefix = getDevPrefix({ debugId, debugFileName }); if (devPrefix) { identifier = `${devPrefix}__${identifier}`; } + + return normalizeIdentifier(identifier); } + if (typeof identOption === 'function') { + identifier = identOption({ + hash: identifier, + debugId, + filePath, + packageName, + }); - return identifier.match(/^[0-9]/) ? `_${identifier}` : identifier; + if (!identifier.match(/^[A-Z_][0-9A-Z_]+$/i)) { + throw new Error( + `Identifier function returned invalid indentifier: "${identifier}"`, + ); + } + + return identifier; + } + + return normalizeIdentifier(identifier); } diff --git a/packages/css/src/types.ts b/packages/css/src/types.ts index 7d89d3ced..078f0b92f 100644 --- a/packages/css/src/types.ts +++ b/packages/css/src/types.ts @@ -1,5 +1,5 @@ -import type { MapLeafNodes, CSSVarFunction } from '@vanilla-extract/private'; -import type { Properties, AtRule } from 'csstype'; +import type { CSSVarFunction, MapLeafNodes } from '@vanilla-extract/private'; +import type { AtRule, Properties } from 'csstype'; import type { SimplePseudos } from './simplePseudos'; @@ -120,7 +120,15 @@ export interface Composition { classList: string; } -type IdentOption = 'short' | 'debug'; +type CustomIdentFunction = (params: { + hash: string; + filePath: string; + debugId?: string; + packageName?: string; +}) => string; + +type IdentOption = 'short' | 'debug' | CustomIdentFunction; + export interface Adapter { appendCss: (css: CSS, fileScope: FileScope) => void; registerClassName: (className: string, fileScope: FileScope) => void; diff --git a/site/docs/integrations/esbuild.md b/site/docs/integrations/esbuild.md index af1d93664..cc1fe8323 100644 --- a/site/docs/integrations/esbuild.md +++ b/site/docs/integrations/esbuild.md @@ -100,6 +100,13 @@ 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 custom identifier function takes an object parameter with properties `hash`, `filePath`, `debugId`, and `packageName`, and returns a customized identifier. e.g. + +```ts +vanillaExtractPlugin({ + identifiers: ({ hash }) => `prefix_${hash}` +}); +``` Each integration will set a default value based on the configuration options passed to the bundler. diff --git a/site/docs/integrations/next.md b/site/docs/integrations/next.md index 9b1c08ba6..ebb1caada 100644 --- a/site/docs/integrations/next.md +++ b/site/docs/integrations/next.md @@ -57,5 +57,12 @@ 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 custom identifier function takes an object parameter with properties `hash`, `filePath`, `debugId`, and `packageName`, and returns a customized identifier. e.g. + +```ts +const withVanillaExtract = createVanillaExtractPlugin({ + identifiers: ({ hash }) => `prefix_${hash}` +}); +``` Each integration will set a default value based on the configuration options passed to the bundler. diff --git a/site/docs/integrations/rollup.md b/site/docs/integrations/rollup.md index 2c1e0b8c5..00bc292ee 100644 --- a/site/docs/integrations/rollup.md +++ b/site/docs/integrations/rollup.md @@ -76,6 +76,13 @@ 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 custom identifier function takes an object parameter with properties `hash`, `filePath`, `debugId`, and `packageName`, and returns a customized identifier. e.g. + +```ts +vanillaExtractPlugin({ + identifiers: ({ hash }) => `prefix_${hash}` +}); +``` Each integration will set a default value based on the configuration options passed to the bundler. diff --git a/site/docs/integrations/vite.md b/site/docs/integrations/vite.md index d600fdc12..456432345 100644 --- a/site/docs/integrations/vite.md +++ b/site/docs/integrations/vite.md @@ -51,6 +51,13 @@ 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 custom identifier function takes an object parameter with properties `hash`, `filePath`, `debugId`, and `packageName`, and returns a customized identifier. e.g. + +```ts +vanillaExtractPlugin({ + identifiers: ({ hash }) => `prefix_${hash}` +}); +``` Each integration will set a default value based on the configuration options passed to the bundler. diff --git a/site/docs/integrations/webpack.md b/site/docs/integrations/webpack.md index a204db252..6665aa12c 100644 --- a/site/docs/integrations/webpack.md +++ b/site/docs/integrations/webpack.md @@ -91,5 +91,12 @@ 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 custom identifier function takes an object parameter with properties `hash`, `filePath`, `debugId`, and `packageName`, and returns a customized identifier. e.g. + +```ts +VanillaExtractPlugin({ + identifiers: ({ hash }) => `prefix_${hash}` +}); +``` Each integration will set a default value based on the configuration options passed to the bundler.