summaryrefslogtreecommitdiff
path: root/@linaria/packages/preeval/src
diff options
context:
space:
mode:
Diffstat (limited to '@linaria/packages/preeval/src')
-rw-r--r--@linaria/packages/preeval/src/babel.ts3
-rw-r--r--@linaria/packages/preeval/src/index.ts88
2 files changed, 91 insertions, 0 deletions
diff --git a/@linaria/packages/preeval/src/babel.ts b/@linaria/packages/preeval/src/babel.ts
new file mode 100644
index 0000000..d99a54b
--- /dev/null
+++ b/@linaria/packages/preeval/src/babel.ts
@@ -0,0 +1,3 @@
+import type core from '@babel/core';
+
+export type Core = typeof core;
diff --git a/@linaria/packages/preeval/src/index.ts b/@linaria/packages/preeval/src/index.ts
new file mode 100644
index 0000000..6197ce0
--- /dev/null
+++ b/@linaria/packages/preeval/src/index.ts
@@ -0,0 +1,88 @@
+/**
+ * This file is a babel preset used to transform files inside evaluators.
+ * It works the same as main `babel/extract` preset, but do not evaluate lazy dependencies.
+ */
+import type { NodePath } from '@babel/traverse';
+import type { Program, Statement, VariableDeclaration } from '@babel/types';
+import type { State, StrictOptions } from '@linaria/babel-preset';
+import {
+ GenerateClassNames,
+ DetectStyledImportName,
+ JSXElement,
+ ProcessStyled,
+ ProcessCSS,
+} from '@linaria/babel-preset';
+import { Core } from './babel';
+
+const isHoistableExport = (
+ node: NodePath<Statement>
+): node is NodePath<Statement> & NodePath<VariableDeclaration> => {
+ // Only `var` can be hoisted
+ if (!node.isVariableDeclaration({ kind: 'var' })) return false;
+
+ const declarations = node.get('declarations');
+
+ // Our target has only one declaration
+ if (!Array.isArray(declarations) || declarations.length !== 1) return false;
+
+ const init = declarations[0].get('init');
+ // It should be initialized with CallExpression…
+ if (!init || Array.isArray(init) || !init.isCallExpression()) return false;
+
+ const callee = init.get('callee');
+ // … which callee should be `required` …
+ if (Array.isArray(callee) || !callee.isIdentifier({ name: 'require' }))
+ return false;
+
+ // … which should be a global identifier
+ return !callee.scope.hasReference('require');
+};
+
+function index(babel: Core, options: StrictOptions) {
+ return {
+ visitor: {
+ Program: {
+ enter(path: NodePath<Program>, state: State) {
+ // Collect all the style rules from the styles we encounter
+ state.queue = [];
+ state.rules = {};
+ state.index = -1;
+ state.dependencies = [];
+ state.replacements = [];
+
+ // We need our transforms to run before anything else
+ // So we traverse here instead of a in a visitor
+ path.traverse({
+ ImportDeclaration: (p) => DetectStyledImportName(babel, p, state),
+ TaggedTemplateExpression: (p) =>
+ GenerateClassNames(babel, p, state, options),
+ JSXElement,
+ });
+ },
+ exit(path: NodePath<Program>) {
+ /* A really dirty hack that solves https://github.com/callstack/linaria/issues/800
+ * Sometimes babel inserts `require` after usages of required modules.
+ * It makes the shaker sad. As a temporary solution, we hoist requires.
+ * This hack should be deleted after transition `shaker` to @babel/traverse
+ */
+ path
+ .get('body')
+ .filter(isHoistableExport)
+ .forEach((p) => {
+ const node = p.node;
+ p.remove();
+ path.unshiftContainer('body', node);
+ });
+ },
+ },
+ CallExpression: ProcessStyled,
+ TaggedTemplateExpression: ProcessCSS, // TaggedTemplateExpression is processed before CallExpression
+ },
+ };
+}
+
+export default function preset(context: any, options: StrictOptions) {
+ return {
+ plugins: [[index, options]],
+ };
+}