Skip to content

Commit 25af4ac

Browse files
authored
refactor: improve runtime alias lookups (#9)
1 parent 88b5a5f commit 25af4ac

16 files changed

+288
-220
lines changed

src/__tests__/codegen.test.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import type { CodegenPrepareHookArgs } from '@pandacss/types';
2+
import { codegen } from '../codegen';
3+
import { createContext } from '../context';
4+
5+
const context = createContext({
6+
foo: { 100: { value: '#fff' }, 200: { value: { base: '#000' } } },
7+
bar: { 100: 'red', 200: 'blue' },
8+
});
9+
10+
const args: CodegenPrepareHookArgs = {
11+
artifacts: [
12+
{
13+
id: 'css-fn',
14+
files: [
15+
{ file: 'css.mjs', code: '' },
16+
{ file: 'css.d.ts', code: '' },
17+
],
18+
},
19+
],
20+
changed: [],
21+
};
22+
23+
describe('codegen', () => {
24+
it('generates ct runtime code', () => {
25+
const result = codegen(args, context) as any[];
26+
expect(result[0].files[0]).toMatchInlineSnapshot(`
27+
{
28+
"code": "
29+
const pluginCtMap = new Map(JSON.parse('[["foo.100","#fff"],["foo.200",{"base":"#000"}],["bar.100","red"],["bar.200","blue"]]'));
30+
31+
export const ct = (path) => {
32+
if (!pluginCtMap.has(path)) return 'panda-plugin-ct-alias-not-found';
33+
return pluginCtMap.get(path);
34+
};
35+
",
36+
"file": "css.mjs",
37+
}
38+
`);
39+
40+
expect(result[0].files[1]).toMatchInlineSnapshot(`
41+
{
42+
"code": "
43+
export const ct: (alias: "foo.100" | "foo.200" | "bar.100" | "bar.200") => string;",
44+
"file": "css.d.ts",
45+
}
46+
`);
47+
});
48+
});

src/__tests__/get.test.ts

Lines changed: 0 additions & 72 deletions
This file was deleted.

src/__tests__/index.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { pluginComponentTokens } from '..';
2+
3+
describe('pluginComponentTokens', () => {
4+
it('returns a PandaPlugin', () => {
5+
expect(pluginComponentTokens).toBeTypeOf('function');
6+
expect(pluginComponentTokens({}).name).toBeDefined();
7+
expect(pluginComponentTokens({}).hooks).toBeDefined();
8+
});
9+
});

src/__tests__/map.test.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { get, makeMap, makePaths, mapTemplate } from '../map';
2+
3+
const tokens = {
4+
foo: { 100: { value: '#fff' }, 200: { value: { base: '#000' } } },
5+
bar: { 100: 'red', 200: 'blue' },
6+
};
7+
8+
describe('get', () => {
9+
it('gets a string', () => {
10+
expect(get(tokens, 'bar.100')).toBe('red');
11+
});
12+
13+
it('gets a value object', () => {
14+
expect(get(tokens, 'foo.200')).toMatchInlineSnapshot(
15+
`
16+
{
17+
"base": "#000",
18+
}
19+
`,
20+
);
21+
});
22+
23+
it('gets a value string', () => {
24+
expect(get(tokens, 'foo.100')).toMatchInlineSnapshot(`"#fff"`);
25+
});
26+
27+
it('gets an undefined token', () => {
28+
expect(get(tokens, 'nope.nope')).toBeUndefined();
29+
expect(get(tokens, 'foo.baz')).toBeUndefined();
30+
});
31+
});
32+
33+
describe('makePaths', () => {
34+
it('makes paths', () => {
35+
expect(makePaths(tokens)).toMatchInlineSnapshot(`
36+
[
37+
"foo.100",
38+
"foo.200",
39+
"bar.100",
40+
"bar.200",
41+
]
42+
`);
43+
});
44+
});
45+
46+
describe('mapTemplate', () => {
47+
it('serializes a Map', () => {
48+
const map = new Map<string, any>([
49+
['foo.100', '#fff'],
50+
['foo.200', { base: '#000' }],
51+
]);
52+
53+
expect(mapTemplate(map)).toMatchInlineSnapshot(
54+
`
55+
"
56+
const pluginCtMap = new Map(JSON.parse('[["foo.100","#fff"],["foo.200",{"base":"#000"}]]'));
57+
"
58+
`,
59+
);
60+
});
61+
});
62+
63+
describe('makeMap', () => {
64+
it('makes a map', () => {
65+
expect(makeMap(tokens)).toMatchInlineSnapshot(`
66+
Map {
67+
"foo.100" => "#fff",
68+
"foo.200" => {
69+
"base": "#000",
70+
},
71+
"bar.100" => "red",
72+
"bar.200" => "blue",
73+
}
74+
`);
75+
});
76+
});

src/__tests__/parser.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { parser } from '../parser';
2+
import { createContext } from '../context';
3+
4+
const context = createContext({
5+
foo: { 100: { value: '#fff' }, 200: { value: { base: '#000' } } },
6+
bar: { 100: 'red', 200: 'blue' },
7+
});
8+
9+
describe('parser', () => {
10+
it('parses', () => {
11+
const res = parser(
12+
{
13+
configure: () => {},
14+
filePath: 'test.tsx',
15+
content: `<div className={css({ bg: ct("foo.200"), color: ct('bar.100'))})/>`,
16+
},
17+
context,
18+
);
19+
20+
expect(res).toMatchInlineSnapshot(
21+
`"<div className={css({ bg: {"base":"#000"}, color: 'red')})/>"`,
22+
);
23+
});
24+
25+
it('skips without "ct(" in contents', () => {
26+
const res = parser(
27+
{
28+
configure: () => {},
29+
filePath: 'test.tsx',
30+
content: `<div className={css({ bg: "red.200" })/>`,
31+
},
32+
context,
33+
);
34+
35+
expect(res).toBeUndefined();
36+
});
37+
});

src/__tests__/tsconfig.json

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/__tests__/utils.test.ts

Lines changed: 14 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,24 @@
1-
import { isObject, makePaths } from '../utils';
1+
import { isObjectWithValue, isObject } from '../utils';
22

3-
describe('isObject', () => {
4-
it('returns true if an object', () => {
5-
expect(isObject({})).toBe(true);
6-
expect(isObject({ foo: 'bar' })).toBe(true);
3+
describe('isObjectWithValue', () => {
4+
it('returns true for an object with a value property', () => {
5+
expect(isObjectWithValue({ value: '' })).toBe(true);
6+
expect(isObjectWithValue({ value: {} })).toBe(true);
77
});
88

9-
it('returns false if not an object', () => {
10-
expect(isObject(1)).toBe(false);
11-
expect(isObject('1')).toBe(false);
12-
expect(isObject(undefined)).toBe(false);
13-
expect(isObject(null)).toBe(false);
14-
expect(isObject([1, 2, 3])).toBe(false);
9+
it('returns false for an object without a value property', () => {
10+
expect(isObjectWithValue({})).toBe(false);
1511
});
1612
});
1713

18-
describe('makePaths', () => {
19-
it('makes paths', () => {
20-
expect(
21-
makePaths({
22-
foo: { 100: { value: '#fff' }, 200: '' },
23-
bar: { 100: '', 200: '' },
24-
}),
25-
).toMatchInlineSnapshot(`
26-
[
27-
"foo.100",
28-
"foo.200",
29-
"bar.100",
30-
"bar.200",
31-
]
32-
`);
14+
describe('isObject', () => {
15+
it('returns true for an object', () => {
16+
expect(isObject({})).toBe(true);
3317
});
3418

35-
it('makes paths with object values', () => {
36-
expect(
37-
makePaths({
38-
foo: { a: { b: { c: { value: { base: '', lg: '' } } } } },
39-
bar: { baz: { 100: { value: '#fff' }, 200: { value: {} } } },
40-
}),
41-
).toMatchInlineSnapshot(`
42-
[
43-
"foo.a.b.c",
44-
"bar.baz.100",
45-
"bar.baz.200",
46-
]
47-
`);
19+
it('returns false for not an object', () => {
20+
expect(isObject([1, 2, 3])).toBe(false);
21+
expect(isObject(null)).toBe(false);
22+
expect(isObject(undefined)).toBe(false);
4823
});
4924
});

src/codegen.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,28 @@ import type {
33
MaybeAsyncReturn,
44
Artifact,
55
} from '@pandacss/types';
6-
import { makePaths } from './utils';
6+
import { makePaths, mapTemplate } from './map';
77
import type { PluginContext } from './types';
8-
import { getTemplate } from './get';
98

109
export const codegen = (
1110
args: CodegenPrepareHookArgs,
12-
context: Partial<PluginContext>,
11+
context: PluginContext,
1312
): MaybeAsyncReturn<void | Artifact[]> => {
14-
const tokens = context.tokens ?? {};
15-
if (!tokens) return;
13+
const { tokens, map } = context;
1614

1715
const cssFn = args.artifacts.find((a) => a.id === 'css-fn');
1816
if (!cssFn) return args.artifacts;
1917

2018
const cssFile = cssFn.files.find((f) => f.file.includes('css.mjs'));
2119
if (!cssFile) return args.artifacts;
2220

23-
cssFile.code += getTemplate(tokens);
21+
cssFile.code += mapTemplate(map);
22+
cssFile.code += `
23+
export const ct = (path) => {
24+
if (!pluginCtMap.has(path)) return 'panda-plugin-ct-alias-not-found';
25+
return pluginCtMap.get(path);
26+
};
27+
`;
2428

2529
const cssDtsFile = cssFn.files.find((f) => f.file.includes('css.d.'));
2630
if (!cssDtsFile) return args.artifacts;

src/create-project.ts renamed to src/context.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { Project, ts } from 'ts-morph';
2+
import type { ComponentTokens, PluginContext } from './types';
3+
import { makeMap } from './map';
24

3-
export const createProject = () => {
4-
return new Project({
5+
export const createContext = (tokens: ComponentTokens): PluginContext => ({
6+
project: new Project({
57
compilerOptions: {
68
jsx: ts.JsxEmit.React,
79
jsxFactory: 'React.createElement',
@@ -16,5 +18,7 @@ export const createProject = () => {
1618
skipAddingFilesFromTsConfig: true,
1719
skipFileDependencyResolution: true,
1820
skipLoadingLibFiles: true,
19-
});
20-
};
21+
}),
22+
tokens,
23+
map: makeMap(tokens),
24+
});

0 commit comments

Comments
 (0)