Skip to content

Commit

Permalink
fix(tags): fix validation for css and styled tags (#1239)
Browse files Browse the repository at this point in the history
fixes #1224
  • Loading branch information
Anber committed Apr 24, 2023
1 parent 2e966f2 commit 1c3f309
Show file tree
Hide file tree
Showing 8 changed files with 52 additions and 23 deletions.
9 changes: 9 additions & 0 deletions .changeset/empty-llamas-flow.md
@@ -0,0 +1,9 @@
---
'@linaria/babel-preset': patch
'@linaria/griffel': patch
'@linaria/react': patch
'@linaria/tags': patch
'@linaria/testkit': patch
---

Fix tags usage validation (fixes #1224)
2 changes: 1 addition & 1 deletion packages/babel/src/utils/getTagProcessor.ts
Expand Up @@ -252,7 +252,7 @@ function getBuilderForIdentifier(
return null;
}

const params: Param[] = [['tag', tagPath.node]];
const params: Param[] = [['callee', tagPath.node]];
let prev: NodePath = tagPath;
let current: NodePath | null = tagPath.parentPath;
while (current && current !== path) {
Expand Down
6 changes: 3 additions & 3 deletions packages/griffel/src/processors/makeStyles.ts
Expand Up @@ -20,12 +20,12 @@ export default class MakeStylesProcessor extends BaseProcessor {
public constructor(params: Params, ...args: TailProcessorParams) {
validateParams(
params,
['tag', 'call'],
['callee', 'call'],
'Invalid usage of `makeStyles` tag'
);
const [tag, callParam] = params;
const [callee, callParam] = params;

super([tag], ...args);
super([callee], ...args);

const { ex } = callParam[1];
if (ex.type === 'Identifier') {
Expand Down
12 changes: 8 additions & 4 deletions packages/react/src/processors/styled.ts
Expand Up @@ -54,12 +54,16 @@ export default class StyledProcessor extends TaggedTemplateProcessor {
#variablesCache = new Map<string, string>();

constructor(params: Params, ...args: TailProcessorParams) {
// If the first param is not a tag, we should skip the expression.
validateParams(params, ['tag', '...'], TaggedTemplateProcessor.SKIP);
// Should have at least two params and the first one should be a callee.
validateParams(
params,
['callee', '*', '...'],
TaggedTemplateProcessor.SKIP
);

validateParams(
params,
['tag', ['call', 'member'], ['template', 'call']],
['callee', ['call', 'member'], ['template', 'call']],
'Invalid usage of `styled` tag'
);

Expand Down Expand Up @@ -184,7 +188,7 @@ export default class StyledProcessor extends TaggedTemplateProcessor {

protected get tagExpression(): CallExpression {
const t = this.astService;
return t.callExpression(this.tag, [this.tagExpressionArgument]);
return t.callExpression(this.callee, [this.tagExpressionArgument]);
}

public override get value(): ObjectExpression {
Expand Down
14 changes: 7 additions & 7 deletions packages/tags/src/BaseProcessor.ts
Expand Up @@ -47,7 +47,7 @@ export default abstract class BaseProcessor {

public readonly slug: string;

protected tag: Identifier | MemberExpression;
protected callee: Identifier | MemberExpression;

protected evaluated:
| Record<'dependencies' | 'expression', Value[]>
Expand Down Expand Up @@ -77,8 +77,8 @@ export default abstract class BaseProcessor {
) {
validateParams(
params,
['tag'],
'Unknown error: a tag param is not specified'
['callee'],
'Unknown error: a callee param is not specified'
);

const { className, slug } = getClassNameAndSlug(
Expand All @@ -91,7 +91,7 @@ export default abstract class BaseProcessor {
this.className = className;
this.slug = slug;

[[, this.tag]] = params;
[[, this.callee]] = params;
}

public abstract build(values: ValueCache): void;
Expand Down Expand Up @@ -129,11 +129,11 @@ export default abstract class BaseProcessor {
public abstract get value(): Expression;

protected tagSourceCode(): string {
if (this.tag.type === 'Identifier') {
return this.tag.name;
if (this.callee.type === 'Identifier') {
return this.callee.name;
}

return generator(this.tag).code;
return generator(this.callee).code;
}

public toString(): string {
Expand Down
10 changes: 7 additions & 3 deletions packages/tags/src/TaggedTemplateProcessor.ts
Expand Up @@ -10,12 +10,16 @@ export default abstract class TaggedTemplateProcessor extends BaseProcessor {
#template: (TemplateElement | ExpressionValue)[];

public constructor(params: Params, ...args: TailProcessorParams) {
// If the first param is not a tag, we should skip the expression.
validateParams(params, ['tag', '...'], TaggedTemplateProcessor.SKIP);
// Should have at least two params and the first one should be a callee.
validateParams(
params,
['callee', '*', '...'],
TaggedTemplateProcessor.SKIP
);

validateParams(
params,
['tag', 'template'],
['callee', 'template'],
'Invalid usage of template tag'
);
const [tag, [, template]] = params;
Expand Down
4 changes: 2 additions & 2 deletions packages/tags/src/types.ts
Expand Up @@ -75,15 +75,15 @@ export type WrappedNode = string | { node: Identifier; source: string };

export type Rules = Record<string, ICSSRule>;

export type TagParam = readonly ['tag', Identifier | MemberExpression];
export type CalleeParam = readonly ['callee', Identifier | MemberExpression];
export type CallParam = readonly ['call', ...ExpressionValue[]];
export type MemberParam = readonly ['member', string];
export type TemplateParam = readonly [
'template',
(TemplateElement | ExpressionValue)[]
];

export type Param = TagParam | CallParam | MemberParam | TemplateParam;
export type Param = CalleeParam | CallParam | MemberParam | TemplateParam;
export type Params = readonly Param[];

export type BuildCodeFrameErrorFn = <TError extends Error>(
Expand Down
18 changes: 15 additions & 3 deletions packages/testkit/src/utils/getTagProcessor.test.ts
Expand Up @@ -7,12 +7,17 @@ import dedent from 'dedent';
import { getTagProcessor } from '@linaria/babel-preset';
import type { BaseProcessor } from '@linaria/tags';

const run = (code: string): BaseProcessor | null => {
interface IRunOptions {
ts?: boolean;
}

const run = (code: string, options: IRunOptions = {}): BaseProcessor | null => {
const opts = {
filename: join(__dirname, 'test.js'),
filename: join(__dirname, options.ts ? 'test.ts' : 'test.js'),
root: '.',
code: true,
ast: true,
presets: options.ts ? ['@babel/preset-typescript'] : [],
};
const rootNode = parseSync(code, opts)!;
let result: BaseProcessor | null = null;
Expand Down Expand Up @@ -352,7 +357,14 @@ describe('getTagProcessor', () => {

it('do not throw if styled is not a tag', () => {
const runner = () =>
run(dedent`export { styled } from "@linaria/react";`);
run(
dedent`
import { styled } from '@linaria/react';
export type StyledReturn = ReturnType<typeof styled>;
`,
{ ts: true }
);

expect(runner).not.toThrow();
});
Expand Down

0 comments on commit 1c3f309

Please sign in to comment.