Skip to content

Commit

Permalink
feat(tags): better support for tags other than tagged templates (#1014)
Browse files Browse the repository at this point in the history
  • Loading branch information
Anber committed Jul 24, 2022
1 parent f0cddda commit 4cdf031
Show file tree
Hide file tree
Showing 29 changed files with 663 additions and 232 deletions.
11 changes: 11 additions & 0 deletions .changeset/spotty-wombats-guess.md
@@ -0,0 +1,11 @@
---
'@linaria/babel-preset': patch
'@linaria/core': patch
'@linaria/griffel': patch
'@linaria/react': patch
'@linaria/server': patch
'@linaria/tags': patch
'@linaria/testkit': patch
---

Tagged template-specific logic has been moved from `BaseProcessor` to `TaggedTemplateProcessor`. `BaseProcessor` now can be used to define any type of expressions for zero-runtime transformations, such as `makeStyles` from `@griffel/react`.
6 changes: 3 additions & 3 deletions package.json
Expand Up @@ -17,8 +17,8 @@
"@changesets/cli": "^2.22.0",
"@commitlint/config-conventional": "^8.3.4",
"@types/jest": "^28.1.0",
"@typescript-eslint/eslint-plugin": "^5.26.0",
"@typescript-eslint/parser": "^5.26.0",
"@typescript-eslint/eslint-plugin": "^5.30.7",
"@typescript-eslint/parser": "^5.30.7",
"all-contributors-cli": "^6.20.0",
"babel-jest": "^28.1.0",
"codecov": "^3.2.0",
Expand All @@ -37,7 +37,7 @@
"eslint-plugin-react-hooks": "^4.5.0",
"husky": "^1.3.1",
"jest": "^28.1.0",
"prettier": "^2.6.2",
"prettier": "^2.7.1",
"react": "^16.14.0",
"syncpack": "^8.0.0",
"turbo": "^1.2.16",
Expand Down
1 change: 1 addition & 0 deletions packages/babel/package.json
Expand Up @@ -6,6 +6,7 @@
"dependencies": {
"@babel/core": "^7.18.9",
"@babel/generator": "^7.18.9",
"@babel/helper-module-imports": "^7.18.6",
"@babel/template": "^7.18.6",
"@babel/traverse": "^7.18.9",
"@babel/types": "^7.18.9",
Expand Down
11 changes: 11 additions & 0 deletions packages/babel/src/helper-module-imports.d.ts
@@ -0,0 +1,11 @@
declare module '@babel/helper-module-imports' {
import type { NodePath } from '@babel/traverse';
import type { Identifier } from '@babel/types';

function addNamed(
path: NodePath,
name: string,
importedSource: string,
opts?: { nameHint: string }
): Identifier;
}
19 changes: 4 additions & 15 deletions packages/babel/src/plugins/preeval.ts
Expand Up @@ -3,7 +3,7 @@
* It works the same as main `babel/extract` preset, but do not evaluate lazy dependencies.
*/
import type { BabelFile, NodePath, PluginObj } from '@babel/core';
import type { TemplateElement, Identifier } from '@babel/types';
import type { Identifier } from '@babel/types';

import { createCustomDebug } from '@linaria/logger';
import type { StrictOptions } from '@linaria/utils';
Expand All @@ -15,7 +15,7 @@ import {
} from '@linaria/utils';

import type { Core } from '../babel';
import type { ExpressionValue, IPluginState } from '../types';
import type { IPluginState } from '../types';
import processTemplateExpression from '../utils/processTemplateExpression';

export type PreevalOptions = Pick<
Expand Down Expand Up @@ -50,10 +50,6 @@ const isBrowserGlobal = (id: NodePath<Identifier>) => {
return forbiddenGlobals.has(id.node.name) && isGlobal(id);
};

const isLazyValue = (
value: ExpressionValue | TemplateElement
): value is ExpressionValue => 'kind' in value;

export default function preeval(
babel: Core,
options: PreevalOptions
Expand Down Expand Up @@ -135,15 +131,8 @@ export default function preeval(
dependencies: [],
};

const expressions: Identifier[] = [];

this.processors.forEach((processor) =>
[
...processor.template.filter(isLazyValue),
...processor.dependencies,
].forEach((dependency) => {
expressions.push(dependency.ex);
})
const expressions: Identifier[] = this.processors.flatMap((processor) =>
processor.dependencies.map((dependency) => dependency.ex)
);

const linariaPreval = file.path.scope.getData('__linariaPreval');
Expand Down
1 change: 0 additions & 1 deletion packages/babel/src/types.ts
Expand Up @@ -8,7 +8,6 @@ import type { BaseProcessor } from '@linaria/tags';
import type { PluginOptions } from './transform-stages/helpers/loadLinariaOptions';

export type {
ComponentDependency,
ExpressionValue,
FunctionValue,
JSONArray,
Expand Down
79 changes: 48 additions & 31 deletions packages/babel/src/utils/getTagProcessor.ts
Expand Up @@ -2,12 +2,18 @@ import { readFileSync } from 'fs';
import { basename, dirname, join } from 'path';

import { types as t } from '@babel/core';
import { addNamed } from '@babel/helper-module-imports';
import type { NodePath } from '@babel/traverse';
import type { Expression, SourceLocation, Identifier } from '@babel/types';
import type {
Expression,
SourceLocation,
Identifier,
MemberExpression,
} from '@babel/types';
import findUp from 'find-up';

import { BaseProcessor } from '@linaria/tags';
import type { Params, IFileContext } from '@linaria/tags';
import type { Param, Params, IFileContext } from '@linaria/tags';
import type { IImport, StrictOptions } from '@linaria/utils';
import {
collectExportsAndImports,
Expand All @@ -22,9 +28,8 @@ import collectTemplateDependencies, {
import getSource from './getSource';

type BuilderArgs = ConstructorParameters<typeof BaseProcessor> extends [
typeof t,
Params,
Expression,
typeof t,
SourceLocation | null,
(replacement: Expression, isPure: boolean) => void,
...infer T
Expand Down Expand Up @@ -154,7 +159,7 @@ function getProcessorForIdentifier(
StrictOptions,
'classNameSlug' | 'displayName' | 'evaluate' | 'tagResolver'
>
): [ProcessorClass, NodePath] | [null, null] {
): [ProcessorClass, NodePath<Identifier | MemberExpression>] | [null, null] {
const pathBinding = path.scope.getBinding(path.node.name);
if (!pathBinding) {
// It's not a binding, so it's not a tag
Expand All @@ -165,28 +170,30 @@ function getProcessorForIdentifier(

// FIXME: can be simplified
const relatedImports = imports
.map((i): [IImport, NodePath | null] | null => {
const { local } = i;
.map(
(i): [IImport, NodePath<Identifier | MemberExpression> | null] | null => {
const { local } = i;

if (local === path) {
return [i, null];
}
if (local === path) {
return [i, null];
}

if (!local.isIdentifier()) {
if (path.isDescendant(local)) {
return [i, local];
}

if (!local.isIdentifier()) {
if (path.isDescendant(local)) {
return [i, local];
return null;
}

return null;
}
const binding = local.scope.getBinding(local.node.name);
if (pathBinding === binding) {
return [i, path];
}

const binding = local.scope.getBinding(local.node.name);
if (pathBinding === binding) {
return [i, path];
return null;
}

return null;
})
)
.filter(isNotNull)
.filter((i) => i[1] === null || i[1].isExpression());

Expand All @@ -196,13 +203,18 @@ function getProcessorForIdentifier(

const [Processor = null, tagPath = null] =
relatedImports
.map(([imp, p]): [ProcessorClass | null, NodePath | null] => {
const source = tagResolver(imp.source, imp.imported);
const processor = source
? getProcessorFromFile(source)
: getProcessorFromPackage(imp.source, imp.imported, filename);
return [processor, p];
})
.map(
([imp, p]): [
ProcessorClass | null,
NodePath<Identifier | MemberExpression> | null
] => {
const source = tagResolver(imp.source, imp.imported);
const processor = source
? getProcessorFromFile(source)
: getProcessorFromPackage(imp.source, imp.imported, filename);
return [processor, p];
}
)
.find(([proc]) => proc) ?? [];

return Processor === null || tagPath === null
Expand Down Expand Up @@ -230,7 +242,7 @@ function getBuilderForIdentifier(
return null;
}

const params: Params = [];
const params: Param[] = [['tag', tagPath.node]];
let prev: NodePath = tagPath;
let current: NodePath | null = tagPath.parentPath;
while (current && current !== path) {
Expand Down Expand Up @@ -306,11 +318,16 @@ function getBuilderForIdentifier(
});
};

const astService = {
...t,
addNamedImport: (name: string, importedSource: string) =>
addNamed(path, name, importedSource),
};

return (...args: BuilderArgs) =>
new Processor(
t,
params,
(tagPath as NodePath<Expression>).node,
astService,
tagPath.node.loc ?? null,
replacer,
...args
Expand Down
20 changes: 4 additions & 16 deletions packages/core/src/processors/css.ts
@@ -1,17 +1,9 @@
import type { Expression, SourceLocation, StringLiteral } from '@babel/types';
import type { SourceLocation, StringLiteral } from '@babel/types';

import type { ProcessorParams, Rules, ValueCache } from '@linaria/tags';
import { BaseProcessor } from '@linaria/tags';

export default class CssProcessor extends BaseProcessor {
constructor(...args: ProcessorParams) {
super(...args);

if (this.params.length !== 1 || this.params[0][0] !== 'template') {
throw new Error('Invalid usage of `css` tag');
}
}
import type { Rules, ValueCache } from '@linaria/tags';
import { TaggedTemplateProcessor } from '@linaria/tags';

export default class CssProcessor extends TaggedTemplateProcessor {
// eslint-disable-next-line class-methods-use-this
public override addInterpolation(node: unknown, source: string): string {
throw new Error(
Expand Down Expand Up @@ -50,10 +42,6 @@ export default class CssProcessor extends BaseProcessor {
return this.className;
}

protected override get tagExpression(): Expression {
return this.tagExp;
}

public override get value(): StringLiteral {
return this.astService.stringLiteral(this.className);
}
Expand Down
3 changes: 3 additions & 0 deletions packages/griffel/babel.config.js
@@ -0,0 +1,3 @@
const config = require('../../babel.config');

module.exports = config;
55 changes: 55 additions & 0 deletions packages/griffel/package.json
@@ -0,0 +1,55 @@
{
"name": "@linaria/griffel",
"description": "Blazing fast zero-runtime CSS in JS library",
"version": "3.0.0-beta.21",
"bugs": "https://github.com/callstack/linaria/issues",
"dependencies": {
"@griffel/core": "^1.5.0",
"@linaria/logger": "workspace:^",
"@linaria/tags": "workspace:^",
"@linaria/utils": "workspace:^",
"ts-invariant": "^0.10.3"
},
"devDependencies": {
"@babel/types": "^7.18.9"
},
"engines": {
"node": "^12.16.0 || >=13.7.0"
},
"files": [
"esm/",
"lib/",
"processors/",
"types/"
],
"homepage": "https://github.com/callstack/linaria#readme",
"keywords": [
"css",
"css-in-js",
"linaria",
"react",
"styled-components"
],
"license": "MIT",
"linaria": {
"tags": {
"makeStyles": "./lib/processors/makeStyles.js"
}
},
"main": "lib/index.js",
"module": "esm/index.js",
"publishConfig": {
"access": "public"
},
"repository": "git@github.com:callstack/linaria.git",
"scripts": {
"build": "npm run build:lib && npm run build:esm && npm run build:declarations",
"build:declarations": "tsc --emitDeclarationOnly --outDir types",
"build:esm": "babel src --out-dir esm --extensions '.js,.jsx,.ts,.tsx' --source-maps --delete-dir-on-start",
"build:lib": "cross-env NODE_ENV=legacy babel src --out-dir lib --extensions '.js,.jsx,.ts,.tsx' --source-maps --delete-dir-on-start",
"typecheck": "tsc --noEmit --composite false",
"watch": "npm run build --watch"
},
"sideEffects": false,
"types": "types/index.d.ts"
}
5 changes: 5 additions & 0 deletions packages/griffel/processors/makeStyles.js
@@ -0,0 +1,5 @@
Object.defineProperty(exports, '__esModule', {
value: true,
});

exports.default = require('../lib/processors/makeStyles').default;
1 change: 1 addition & 0 deletions packages/griffel/src/index.ts
@@ -0,0 +1 @@
export { default as makeStyles } from './makeStyles';
6 changes: 6 additions & 0 deletions packages/griffel/src/makeStyles.ts
@@ -0,0 +1,6 @@
export default function makeStyles<Slots extends string | number>(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
stylesBySlots: Record<Slots, unknown>
): () => Record<Slots, string> {
throw new Error('Cannot be called in runtime');
}

0 comments on commit 4cdf031

Please sign in to comment.