Skip to content

Commit

Permalink
[core] Implement useTheme processor and runtime theme (#105)
Browse files Browse the repository at this point in the history
  • Loading branch information
brijeshb42 authored Jun 4, 2024
1 parent df6e7ef commit ab5aabb
Show file tree
Hide file tree
Showing 19 changed files with 226 additions and 101 deletions.
5 changes: 5 additions & 0 deletions packages/pigment-css-react/exports/useTheme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Object.defineProperty(exports, '__esModule', {
value: true,
});

exports.default = require('../processors/useTheme').UseThemeProcessor;
8 changes: 5 additions & 3 deletions packages/pigment-css-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@
"@emotion/react": "^11.11.4",
"@emotion/serialize": "^1.1.4",
"@emotion/styled": "^11.11.5",
"@mui/system": "^6.0.0-alpha.1",
"@mui/utils": "^6.0.0-alpha.1",
"@mui/system": "^6.0.0-alpha.6",
"@mui/utils": "^6.0.0-alpha.6",
"@wyw-in-js/processor-utils": "^0.5.3",
"@wyw-in-js/shared": "^0.5.3",
"@wyw-in-js/transform": "^0.5.3",
Expand Down Expand Up @@ -86,7 +86,9 @@
"generateAtomics": "./exports/generateAtomics.js",
"css": "./exports/css.js",
"createUseThemeProps": "./exports/createUseThemeProps.js",
"internal_createExtendSxProp": "./exports/internal_createExtendSxProp.js"
"internal_createExtendSxProp": "./exports/internal_createExtendSxProp.js",
"globalCss": "./exports/globalCss.js",
"useTheme": "./exports/useTheme.js"
}
},
"files": [
Expand Down
36 changes: 1 addition & 35 deletions packages/pigment-css-react/src/RtlProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,3 @@
'use client';
/**
* This package has it's own version of RtlProvider to avoid including
* @mui/system in the bundle if someone is not using it.
*/
import * as React from 'react';
import PropTypes from 'prop-types';

type RtlContextType = boolean | undefined;

type RtlProviderProps = {
value?: RtlContextType;
};

const RtlContext = React.createContext<RtlContextType>(false);

function RtlProvider({ value, ...props }: RtlProviderProps) {
return <RtlContext.Provider value={value ?? true} {...props} />;
}

RtlProvider.propTypes /* remove-proptypes */ = {
// ┌────────────────────────────── Warning ──────────────────────────────┐
// │ These PropTypes are generated from the TypeScript type definitions. │
// │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
// └─────────────────────────────────────────────────────────────────────┘
/**
* @ignore
*/
value: PropTypes.bool,
} as any;

export const useRtl = () => {
const value = React.useContext(RtlContext);
return value ?? false;
};

export default RtlProvider;
export * from '@mui/system/RtlProvider';
1 change: 1 addition & 0 deletions packages/pigment-css-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export { default as css } from './css';
export { default as createUseThemeProps } from './createUseThemeProps';
export { default as internal_createExtendSxProp } from './createExtendSxProp';
export { default as Box } from './Box';
export { default as useTheme } from './useTheme';
61 changes: 61 additions & 0 deletions packages/pigment-css-react/src/processors/useTheme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { Expression } from '@babel/types';
import { validateParams, type Params, type TailProcessorParams } from '@wyw-in-js/processor-utils';
import { type Replacements, type Rules } from '@wyw-in-js/shared';
import BaseProcessor from './base-processor';

export type Primitive = string | number | boolean | null | undefined;

export type TemplateCallback = (params: Record<string, unknown> | undefined) => string | number;

export class UseThemeProcessor extends BaseProcessor {
constructor(params: Params, ...args: TailProcessorParams) {
super([params[0]], ...args);
validateParams(params, ['callee', ['call']], `Invalid use of ${this.tagSource.imported} tag.`);
}

build() {
const cssText = '/* */';
const rules: Rules = {
[this.asSelector]: {
className: this.className,
cssText,
displayName: this.displayName,
start: this.location?.start ?? null,
},
};
const sourceMapReplacements: Replacements = [
{
length: cssText.length,
original: {
start: {
column: this.location?.start.column ?? 0,
line: this.location?.start.line ?? 0,
},
end: {
column: this.location?.end.column ?? 0,
line: this.location?.end.line ?? 0,
},
},
},
];
this.artifacts.push(['css', [rules, sourceMapReplacements]]);
}

doEvaltimeReplacement() {
this.replacer(this.value, false);
}

doRuntimeReplacement() {
const t = this.astService;
const themeIdentifier = t.addDefaultImport(`${process.env.PACKAGE_NAME as string}/theme`);
this.replacer(themeIdentifier, false);
}

get asSelector() {
return this.className;
}

get value(): Expression {
return this.astService.nullLiteral();
}
}
3 changes: 3 additions & 0 deletions packages/pigment-css-react/src/useTheme.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { ThemeArgs } from './theme';

export default function useTheme(): ThemeArgs extends { theme: any } ? ThemeArgs['theme'] : any;
5 changes: 5 additions & 0 deletions packages/pigment-css-react/src/useTheme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default function useTheme() {
throw new Error(
`${process.env.PACKAGE_NAME}: You were trying to call "useTheme" function without configuring your bundler. Make sure to install the bundler specific plugin and use it. @pigment-css/vite-plugin for Vite integration or @pigment-css/nextjs-plugin for Next.js integration.`,
);
}
12 changes: 12 additions & 0 deletions packages/pigment-css-react/src/utils/generateThemeSource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { Theme } from './extendTheme';
import { generateThemeTokens } from './generateCss';

export function generateThemeSource(theme?: Theme) {
if (!theme) {
return `export default {}`;
}
if (typeof theme.toRuntimeSource !== 'function') {
return `export default ${JSON.stringify(generateThemeTokens(theme))};`;
}
return theme.toRuntimeSource.call(theme, theme);
}
1 change: 1 addition & 0 deletions packages/pigment-css-react/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export type { PluginCustomOptions } from './cssFnValueToVariable';
export * from './preprocessor';
export * from './generateCss';
export * from './extendTheme';
export * from './generateThemeSource';
3 changes: 2 additions & 1 deletion packages/pigment-css-react/tests/testUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { PluginCustomOptions, preprocessor } from '@pigment-css/react/utils';
import * as prettier from 'prettier';

import sxTransformPlugin from '../exports/sx-plugin';
import pkgJson from '../package.json';

const shouldUpdateOutput = process.env.UPDATE_FIXTURES === 'true';

Expand Down Expand Up @@ -55,7 +56,7 @@ export async function runTransformation(
if (source !== '@pigment-css/react' && !source.endsWith('/zero-styled')) {
return null;
}
return require.resolve(`../exports/${tag}`);
return require.resolve(`../${pkgJson['wyw-in-js'].tags[tag]}`.replace('.js', ''));
},
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { useTheme } from '../zero-styled';

export const Fade = React.forwardRef(function Fade(props, ref) {
const theme = useTheme();
return <div style={{ backgroundColor: theme.palette.primary.main }} />;
});
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import _default from '@pigment-css/react/theme';
export const Fade = React.forwardRef(function Fade(props, ref) {
const theme = _default;
return (
<div
style={{
backgroundColor: theme.palette.primary.main,
}}
/>
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { expect } from 'chai';
import { generateThemeSource } from '../../src/utils';

describe('Pigment CSS - generateThemeSource', () => {
it('should export empty theme if theme is undefined', () => {
expect(generateThemeSource()).to.equal(`export default {}`);
});

it('should generate basic theme code with theme json', () => {
const result = generateThemeSource({
vars: {
palette: {
primary: {
main: 'red',
secondary: 'blue',
},
},
},
});

expect(result).to.equal(
`export default {"vars":{"palette":{"primary":{"main":"red","secondary":"blue"}}}};`,
);
});

it('should generate theme from toRuntimeSource method if present', () => {
const result = generateThemeSource({
vars: {
palette: {
primary: {
main: 'red',
secondary: 'blue',
},
},
},
toRuntimeSource(theme: any) {
return `const theme = ${JSON.stringify(theme)};export default theme;`;
},
});

expect(result).to.equal(
`const theme = {"vars":{"palette":{"primary":{"main":"red","secondary":"blue"}}}};export default theme;`,
);
});
});
13 changes: 13 additions & 0 deletions packages/pigment-css-react/tests/useTheme/useTheme.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import path from 'node:path';
import { runTransformation, expect } from '../testUtils';

describe('Pigment CSS - useTheme', () => {
it('should replace useTheme call with the theme import', async () => {
const { output, fixture } = await runTransformation(
path.join(__dirname, 'fixtures/useTheme.input.js'),
);

expect(output.js).to.equal(fixture.js);
expect(output.css).to.equal(fixture.css);
});
});
1 change: 1 addition & 0 deletions packages/pigment-css-react/tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const processors = [
'createUseThemeProps',
'createExtendSxProp',
'globalCss',
'useTheme',
];
const external = ['react', 'react-is', 'prop-types'];

Expand Down
10 changes: 3 additions & 7 deletions packages/pigment-css-unplugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
import {
preprocessor as basePreprocessor,
generateTokenCss,
generateThemeTokens,
generateThemeSource,
extendTheme,
type Theme as BaseTheme,
type PluginCustomOptions,
Expand Down Expand Up @@ -348,9 +348,7 @@ export const plugin = createUnplugin<PigmentOptions, true>((options) => {
return theme ? generateTokenCss(theme) : _code;
}
if (id.includes('pigment-css-react/theme')) {
return `export default ${
theme ? JSON.stringify(generateThemeTokens(theme)) : '{}'
};`;
return generateThemeSource(theme);
}
return null;
},
Expand All @@ -373,9 +371,7 @@ export const plugin = createUnplugin<PigmentOptions, true>((options) => {
return generateTokenCss(theme);
}
if (id === VIRTUAL_THEME_FILE) {
return `export default ${
theme ? JSON.stringify(generateThemeTokens(theme)) : '{}'
};`;
return generateThemeSource(theme);
}
return null;
},
Expand Down
4 changes: 2 additions & 2 deletions packages/pigment-css-vite-plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import type { Plugin } from 'vite';
import {
preprocessor as basePreprocessor,
generateTokenCss,
generateThemeTokens,
type Theme,
extendTheme,
generateThemeSource,
} from '@pigment-css/react/utils';
import { transformAsync } from '@babel/core';
import baseWywPluginPlugin, { type VitePluginOptions } from './vite-plugin';
Expand Down Expand Up @@ -65,7 +65,7 @@ export function pigment(options: PigmentOptions) {
return generateTokenCss(theme);
}
if (id === VIRTUAL_THEME_FILE) {
return `export default ${JSON.stringify(generateThemeTokens(theme))};`;
return generateThemeSource(theme);
}
return null;
},
Expand Down
Loading

0 comments on commit ab5aabb

Please sign in to comment.