Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tags): better support for tags other than tagged templates #1014

Merged
merged 1 commit into from
Jul 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/spotty-wombats-guess.md
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const config = require('../../babel.config');

module.exports = config;
55 changes: 55 additions & 0 deletions packages/griffel/package.json
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as makeStyles } from './makeStyles';
6 changes: 6 additions & 0 deletions packages/griffel/src/makeStyles.ts
Original file line number Diff line number Diff line change
@@ -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');
}