summaryrefslogtreecommitdiff
path: root/@linaria/packages/babel/src/evaluators/visitors
diff options
context:
space:
mode:
Diffstat (limited to '@linaria/packages/babel/src/evaluators/visitors')
-rw-r--r--@linaria/packages/babel/src/evaluators/visitors/JSXElement.ts56
-rw-r--r--@linaria/packages/babel/src/evaluators/visitors/ProcessCSS.ts20
-rw-r--r--@linaria/packages/babel/src/evaluators/visitors/ProcessStyled.ts46
3 files changed, 122 insertions, 0 deletions
diff --git a/@linaria/packages/babel/src/evaluators/visitors/JSXElement.ts b/@linaria/packages/babel/src/evaluators/visitors/JSXElement.ts
new file mode 100644
index 0000000..89a0ccd
--- /dev/null
+++ b/@linaria/packages/babel/src/evaluators/visitors/JSXElement.ts
@@ -0,0 +1,56 @@
+import { types as t } from '@babel/core';
+import type { NodePath } from '@babel/traverse';
+import type { Function, JSXElement as JSXElementNode } from '@babel/types';
+
+function getFunctionName(path: NodePath<Function>): string | null {
+ if (path.isClassMethod() && t.isIdentifier(path.node.key)) {
+ return path.node.key.name;
+ }
+
+ return null;
+}
+
+export default function JSXElement(path: NodePath<JSXElementNode>) {
+ // JSX can be safely replaced on an empty fragment because it is unnecessary for styles
+ const emptyFragment = t.jsxFragment(
+ t.jsxOpeningFragment(),
+ t.jsxClosingFragment(),
+ []
+ );
+
+ // We can do even more
+ // If that JSX is a result of a function, we can replace the function body.
+ const scopePath = path.scope.path;
+ if (scopePath.isFunction()) {
+ const emptyBody = t.blockStatement([t.returnStatement(emptyFragment)]);
+
+ // Is it not just a function, but a method `render`?
+ if (getFunctionName(scopePath) === 'render') {
+ const decl = scopePath.findParent((p) => p.isClassDeclaration());
+
+ // Replace the whole component
+ if (decl?.isClassDeclaration()) {
+ decl.replaceWith(t.functionDeclaration(decl.node.id, [], emptyBody));
+
+ return;
+ }
+ }
+
+ const body = scopePath.get('body');
+ if (Array.isArray(body)) {
+ throw new Error(
+ `A body of a function is expected to be a single element but an array was returned. It's possible if JS syntax has been changed since that code was written.`
+ );
+ }
+
+ const node: typeof scopePath.node = {
+ ...scopePath.node,
+ body: emptyBody,
+ params: [],
+ };
+
+ scopePath.replaceWith(node);
+ } else {
+ path.replaceWith(emptyFragment);
+ }
+}
diff --git a/@linaria/packages/babel/src/evaluators/visitors/ProcessCSS.ts b/@linaria/packages/babel/src/evaluators/visitors/ProcessCSS.ts
new file mode 100644
index 0000000..6f737a1
--- /dev/null
+++ b/@linaria/packages/babel/src/evaluators/visitors/ProcessCSS.ts
@@ -0,0 +1,20 @@
+/**
+ * This visitor replaces css tag with the generated className
+ *
+ */
+
+import { types as t } from '@babel/core';
+import type { NodePath } from '@babel/traverse';
+import type { TaggedTemplateExpression } from '@babel/types';
+import getLinariaComment from '../../utils/getLinariaComment';
+
+export default function ProcessCSS(path: NodePath<TaggedTemplateExpression>) {
+ if (t.isIdentifier(path.node.tag) && path.node.tag.name === 'css') {
+ const [, , , className] = getLinariaComment(path);
+ if (!className) {
+ return;
+ }
+
+ path.replaceWith(t.stringLiteral(className));
+ }
+}
diff --git a/@linaria/packages/babel/src/evaluators/visitors/ProcessStyled.ts b/@linaria/packages/babel/src/evaluators/visitors/ProcessStyled.ts
new file mode 100644
index 0000000..a9e38c5
--- /dev/null
+++ b/@linaria/packages/babel/src/evaluators/visitors/ProcessStyled.ts
@@ -0,0 +1,46 @@
+/**
+ * This visitor replaces styled components with metadata about them.
+ * CallExpression should be used to match styled components.
+ * Works out of the box for styled that wraps other component,
+ * styled.tagName are transformed to call expressions using @babel/plugin-transform-template-literals
+ * @babel/plugin-transform-template-literals is loaded as a prest, to force proper ordering. It has to run just after linaria.
+ * It is used explicitly in extractor, and loaded as a part of `prest-env` in shaker
+ */
+
+import { types as t } from '@babel/core';
+import type { NodePath } from '@babel/traverse';
+import type { CallExpression } from '@babel/types';
+import { expression } from '@babel/template';
+import getLinariaComment from '../../utils/getLinariaComment';
+
+const linariaComponentTpl = expression(
+ `{
+ displayName: %%displayName%%,
+ __linaria: {
+ className: %%className%%,
+ extends: %%extends%%
+ }
+ }`
+);
+
+export default function ProcessStyled(path: NodePath<CallExpression>) {
+ const [type, , displayName, className] = getLinariaComment(path);
+ if (!className) {
+ return;
+ }
+
+ if (type === 'css') {
+ path.replaceWith(t.stringLiteral(className));
+ return;
+ }
+
+ path.replaceWith(
+ linariaComponentTpl({
+ className: t.stringLiteral(className),
+ displayName: displayName ? t.stringLiteral(displayName) : null,
+ extends: t.isCallExpression(path.node.callee)
+ ? path.node.callee.arguments[0]
+ : t.nullLiteral(),
+ })
+ );
+}