From 248081ee26c3fc18f93a19d03fdd2cb8b816b0c2 Mon Sep 17 00:00:00 2001 From: Anton Evzhakov Date: Fri, 22 Jul 2022 12:47:15 +0300 Subject: [PATCH] feat(tags): better support for tags other than tagged templates --- .changeset/spotty-wombats-guess.md | 11 ++ package.json | 6 +- packages/babel/package.json | 1 + packages/babel/src/helper-module-imports.d.ts | 11 ++ packages/babel/src/plugins/preeval.ts | 19 +- packages/babel/src/types.ts | 1 - packages/babel/src/utils/getTagProcessor.ts | 79 ++++---- packages/core/src/processors/css.ts | 20 +-- packages/griffel/babel.config.js | 3 + packages/griffel/package.json | 55 ++++++ packages/griffel/processors/makeStyles.js | 5 + packages/griffel/src/index.ts | 1 + packages/griffel/src/makeStyles.ts | 6 + packages/griffel/src/processors/makeStyles.ts | 102 +++++++++++ packages/griffel/tsconfig.json | 5 + packages/react/src/processors/styled.ts | 51 ++++-- packages/server/package.json | 2 +- packages/tags/package.json | 2 + packages/tags/src/BaseProcessor.ts | 88 ++++----- packages/tags/src/TaggedTemplateProcessor.ts | 65 +++++++ packages/tags/src/index.ts | 2 + packages/tags/src/types.ts | 31 ++-- packages/tags/src/utils/templateProcessor.ts | 4 +- packages/tags/src/utils/validateParams.ts | 61 +++++++ packages/testkit/package.json | 1 + .../src/__snapshots__/babel.test.ts.snap | 26 +++ packages/testkit/src/babel.test.ts | 23 +++ .../testkit/src/utils/getTagProcessor.test.ts | 44 +++-- pnpm-lock.yaml | 170 +++++++++++------- 29 files changed, 663 insertions(+), 232 deletions(-) create mode 100644 .changeset/spotty-wombats-guess.md create mode 100644 packages/babel/src/helper-module-imports.d.ts create mode 100644 packages/griffel/babel.config.js create mode 100644 packages/griffel/package.json create mode 100644 packages/griffel/processors/makeStyles.js create mode 100644 packages/griffel/src/index.ts create mode 100644 packages/griffel/src/makeStyles.ts create mode 100644 packages/griffel/src/processors/makeStyles.ts create mode 100644 packages/griffel/tsconfig.json create mode 100644 packages/tags/src/TaggedTemplateProcessor.ts create mode 100644 packages/tags/src/utils/validateParams.ts diff --git a/.changeset/spotty-wombats-guess.md b/.changeset/spotty-wombats-guess.md new file mode 100644 index 000000000..bb955405d --- /dev/null +++ b/.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`. diff --git a/package.json b/package.json index 40afb560b..3b0e9d02c 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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", diff --git a/packages/babel/package.json b/packages/babel/package.json index adbec2172..ce8961abf 100644 --- a/packages/babel/package.json +++ b/packages/babel/package.json @@ -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", diff --git a/packages/babel/src/helper-module-imports.d.ts b/packages/babel/src/helper-module-imports.d.ts new file mode 100644 index 000000000..b27fcf17e --- /dev/null +++ b/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; +} diff --git a/packages/babel/src/plugins/preeval.ts b/packages/babel/src/plugins/preeval.ts index 5af65ecd9..6fe0a77f0 100644 --- a/packages/babel/src/plugins/preeval.ts +++ b/packages/babel/src/plugins/preeval.ts @@ -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'; @@ -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< @@ -50,10 +50,6 @@ const isBrowserGlobal = (id: NodePath) => { 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 @@ -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'); diff --git a/packages/babel/src/types.ts b/packages/babel/src/types.ts index af3f7bd3c..51038f043 100644 --- a/packages/babel/src/types.ts +++ b/packages/babel/src/types.ts @@ -8,7 +8,6 @@ import type { BaseProcessor } from '@linaria/tags'; import type { PluginOptions } from './transform-stages/helpers/loadLinariaOptions'; export type { - ComponentDependency, ExpressionValue, FunctionValue, JSONArray, diff --git a/packages/babel/src/utils/getTagProcessor.ts b/packages/babel/src/utils/getTagProcessor.ts index bf41a8c7e..d109bf51e 100644 --- a/packages/babel/src/utils/getTagProcessor.ts +++ b/packages/babel/src/utils/getTagProcessor.ts @@ -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, @@ -22,9 +28,8 @@ import collectTemplateDependencies, { import getSource from './getSource'; type BuilderArgs = ConstructorParameters extends [ - typeof t, Params, - Expression, + typeof t, SourceLocation | null, (replacement: Expression, isPure: boolean) => void, ...infer T @@ -154,7 +159,7 @@ function getProcessorForIdentifier( StrictOptions, 'classNameSlug' | 'displayName' | 'evaluate' | 'tagResolver' > -): [ProcessorClass, NodePath] | [null, null] { +): [ProcessorClass, NodePath] | [null, null] { const pathBinding = path.scope.getBinding(path.node.name); if (!pathBinding) { // It's not a binding, so it's not a tag @@ -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 | 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()); @@ -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 | 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 @@ -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) { @@ -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).node, + astService, tagPath.node.loc ?? null, replacer, ...args diff --git a/packages/core/src/processors/css.ts b/packages/core/src/processors/css.ts index b959d229c..d3b31cd62 100644 --- a/packages/core/src/processors/css.ts +++ b/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( @@ -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); } diff --git a/packages/griffel/babel.config.js b/packages/griffel/babel.config.js new file mode 100644 index 000000000..c9ad680b1 --- /dev/null +++ b/packages/griffel/babel.config.js @@ -0,0 +1,3 @@ +const config = require('../../babel.config'); + +module.exports = config; diff --git a/packages/griffel/package.json b/packages/griffel/package.json new file mode 100644 index 000000000..911271592 --- /dev/null +++ b/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" +} diff --git a/packages/griffel/processors/makeStyles.js b/packages/griffel/processors/makeStyles.js new file mode 100644 index 000000000..33a4817a9 --- /dev/null +++ b/packages/griffel/processors/makeStyles.js @@ -0,0 +1,5 @@ +Object.defineProperty(exports, '__esModule', { + value: true, +}); + +exports.default = require('../lib/processors/makeStyles').default; diff --git a/packages/griffel/src/index.ts b/packages/griffel/src/index.ts new file mode 100644 index 000000000..ae8a76343 --- /dev/null +++ b/packages/griffel/src/index.ts @@ -0,0 +1 @@ +export { default as makeStyles } from './makeStyles'; diff --git a/packages/griffel/src/makeStyles.ts b/packages/griffel/src/makeStyles.ts new file mode 100644 index 000000000..a4233aa47 --- /dev/null +++ b/packages/griffel/src/makeStyles.ts @@ -0,0 +1,6 @@ +export default function makeStyles( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + stylesBySlots: Record +): () => Record { + throw new Error('Cannot be called in runtime'); +} diff --git a/packages/griffel/src/processors/makeStyles.ts b/packages/griffel/src/processors/makeStyles.ts new file mode 100644 index 000000000..57e355d39 --- /dev/null +++ b/packages/griffel/src/processors/makeStyles.ts @@ -0,0 +1,102 @@ +/* eslint-disable class-methods-use-this */ +import type { Expression } from '@babel/types'; +import { resolveStyleRulesForSlots } from '@griffel/core'; +import type { + StylesBySlots, + CSSClassesMapBySlot, + CSSRulesByBucket, +} from '@griffel/core/types'; + +import type { ValueCache, Params, TailProcessorParams } from '@linaria/tags'; +import { BaseProcessor, validateParams } from '@linaria/tags'; + +export default class MakeStylesProcessor extends BaseProcessor { + #cssClassMap: CSSClassesMapBySlot | undefined; + + #cssRulesByBucket: CSSRulesByBucket | undefined; + + readonly #slotsExpName: string; + + public constructor(params: Params, ...args: TailProcessorParams) { + validateParams( + params, + ['tag', 'call'], + 'Invalid usage of `makeStyles` tag' + ); + const [tag, callParam] = params; + + super([tag], ...args); + + this.#slotsExpName = callParam[1].ex.name; + } + + public override get asSelector(): string { + throw new Error('The result of makeStyles cannot be used as a selector.'); + } + + public override build(valueCache: ValueCache) { + const slots = valueCache.get(this.#slotsExpName) as StylesBySlots; + [this.#cssClassMap, this.#cssRulesByBucket] = + resolveStyleRulesForSlots(slots); + } + + public override doEvaltimeReplacement(): void { + this.replacer(this.value, false); + } + + public override doRuntimeReplacement(): void { + if (!this.#cssClassMap || !this.#cssRulesByBucket) { + throw new Error( + 'Styles are not extracted yet. Please call `build` first.' + ); + } + + const t = this.astService; + + const importedStyles = t.addNamedImport('__styles', '@griffel/react'); + + const cssClassMap = t.objectExpression( + Object.entries(this.#cssClassMap).map(([slot, classesMap]) => { + return t.objectProperty( + t.identifier(slot), + t.objectExpression( + Object.entries(classesMap).map(([className, classValue]) => + t.objectProperty( + t.identifier(className), + Array.isArray(classValue) + ? t.arrayExpression(classValue.map((i) => t.stringLiteral(i))) + : t.stringLiteral(classValue) + ) + ) + ) + ); + }) + ); + + const cssRulesByBucket = t.objectExpression( + Object.entries(this.#cssRulesByBucket).map(([bucket, rules]) => { + return t.objectProperty( + t.identifier(bucket), + t.arrayExpression( + // FIXME: rule can be [string, Record] + rules.map((rule) => t.stringLiteral(rule as string)) + ) + ); + }) + ); + + const stylesCall = t.callExpression(importedStyles, [ + cssClassMap, + cssRulesByBucket, + ]); + this.replacer(stylesCall, true); + } + + public override get value(): Expression { + return this.astService.nullLiteral(); + } + + public override toString(): string { + return `${super.toString()}(…)`; + } +} diff --git a/packages/griffel/tsconfig.json b/packages/griffel/tsconfig.json new file mode 100644 index 000000000..6d0f1dbc8 --- /dev/null +++ b/packages/griffel/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { "paths": {}, "rootDir": "src/" }, + "references": [{ "path": "../core" }, { "path": "../logger" }, { "path": "../react" }, { "path": "../utils" }] +} diff --git a/packages/react/src/processors/styled.ts b/packages/react/src/processors/styled.ts index c77b1c637..86f11dd8d 100644 --- a/packages/react/src/processors/styled.ts +++ b/packages/react/src/processors/styled.ts @@ -6,12 +6,18 @@ import type { StringLiteral, } from '@babel/types'; -import { BaseProcessor, ValueType, hasMeta } from '@linaria/tags'; import type { Rules, WrappedNode, ValueCache, - ProcessorParams, + Params, + TailProcessorParams, +} from '@linaria/tags'; +import { + TaggedTemplateProcessor, + ValueType, + hasMeta, + validateParams, } from '@linaria/tags'; const isNotNull = (x: T | null): x is T => x !== null; @@ -32,19 +38,27 @@ const singleQuotedStringLiteral = (value: string): StringLiteral => ({ }, }); -export default class StyledProcessor extends BaseProcessor { +export default class StyledProcessor extends TaggedTemplateProcessor { public component: WrappedNode; #variableIdx = 0; #variablesCache = new Map(); - constructor(...args: ProcessorParams) { - super(...args); + constructor(params: Params, ...args: TailProcessorParams) { + validateParams( + params, + ['tag', ['call', 'member'], 'template'], + 'Invalid usage of `styled` tag' + ); + + const [tag, tagOp, template] = params; + + super([tag, template], ...args); let component: WrappedNode | undefined; - const [type, value, ...rest] = this.params[0] ?? []; - if (type === 'call' && rest.length === 0) { + if (tagOp[0] === 'call' && tagOp.length === 2) { + const value = tagOp[1]; if (value.kind === ValueType.FUNCTION) { component = 'FunctionalComponent'; } else { @@ -57,12 +71,11 @@ export default class StyledProcessor extends BaseProcessor { } } - if (type === 'member') { - component = value; + if (tagOp[0] === 'member') { + [, component] = tagOp; } - // FIXME: stricter validation — component must have a template param - if (!component || this.params.length > 2) { + if (!component) { throw new Error('Invalid usage of `styled` tag'); } @@ -151,7 +164,7 @@ export default class StyledProcessor extends BaseProcessor { protected get tagExpression(): CallExpression { const t = this.astService; - return t.callExpression(this.tagExp, [this.tagExpressionArgument]); + return t.callExpression(this.tag, [this.tagExpressionArgument]); } public override get value(): ObjectExpression { @@ -182,6 +195,20 @@ export default class StyledProcessor extends BaseProcessor { ]); } + public override toString(): string { + const res = (arg: string) => `${this.tagSourceCode()}(${arg})\`…\``; + + if (typeof this.component === 'string') { + if (this.component === 'FunctionalComponent') { + return res('() => {…}'); + } + + return res(`'${this.component}'`); + } + + return res(this.component.source); + } + protected getProps(): IProps { const propsObj: IProps = { name: this.displayName, diff --git a/packages/server/package.json b/packages/server/package.json index 92fcae09e..5514c6769 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -9,7 +9,7 @@ "devDependencies": { "@types/dedent": "^0.7.0", "dedent": "^0.7.0", - "prettier": "^2.6.2" + "prettier": "^2.7.1" }, "engines": { "node": "^12.16.0 || >=13.7.0" diff --git a/packages/tags/package.json b/packages/tags/package.json index d61e7e652..93aec6169 100644 --- a/packages/tags/package.json +++ b/packages/tags/package.json @@ -4,6 +4,7 @@ "version": "4.0.0-beta.1", "bugs": "https://github.com/callstack/linaria/issues", "dependencies": { + "@babel/generator": "^7.18.9", "@linaria/logger": "workspace:^", "@linaria/utils": "workspace:^" }, @@ -11,6 +12,7 @@ "@babel/traverse": "^7.18.9", "@babel/types": "^7.18.9", "@types/babel__core": "^7.1.19", + "@types/babel__generator": "^7.6.4", "@types/babel__traverse": "^7.17.1", "@types/node": "^17.0.39" }, diff --git a/packages/tags/src/BaseProcessor.ts b/packages/tags/src/BaseProcessor.ts index 1a2e20b07..336097a09 100644 --- a/packages/tags/src/BaseProcessor.ts +++ b/packages/tags/src/BaseProcessor.ts @@ -1,31 +1,33 @@ /* eslint-disable class-methods-use-this */ import type { types as t } from '@babel/core'; -import type { SourceLocation, Expression, TemplateElement } from '@babel/types'; +import generator from '@babel/generator'; +import type { + Expression, + Identifier, + SourceLocation, + MemberExpression, +} from '@babel/types'; import type { ExpressionValue, IInterpolation, - IPlaceholder, - Param, Params, - Rules, Value, ValueCache, Artifact, - TemplateParam, } from './types'; import getClassNameAndSlug from './utils/getClassNameAndSlug'; import hasMeta from './utils/hasMeta'; -import templateProcessor from './utils/templateProcessor'; import { isCSSable } from './utils/toCSS'; import type { IFileContext, IOptions } from './utils/types'; +import { validateParams } from './utils/validateParams'; export { Expression }; export type ProcessorParams = ConstructorParameters; - -const isTemplateParam = (param: Param): param is TemplateParam => - param[0] === 'template'; +export type TailProcessorParams = ProcessorParams extends [Params, ...infer T] + ? T + : never; export default abstract class BaseProcessor { public readonly artifacts: Artifact[] = []; @@ -36,18 +38,19 @@ export default abstract class BaseProcessor { public interpolations: IInterpolation[] = []; - #placeholders: IPlaceholder[] = []; - public readonly slug: string; + protected tag: Identifier | MemberExpression; + protected evaluated: | Record<'dependencies' | 'expression', Value[]> | undefined; public constructor( - protected readonly astService: typeof t, - protected readonly params: Params, - protected readonly tagExp: Expression, + params: Params, + protected readonly astService: typeof t & { + addNamedImport: (name: string, source: string) => Identifier; + }, public readonly location: SourceLocation | null, protected readonly replacer: ( replacement: Expression, @@ -59,6 +62,12 @@ export default abstract class BaseProcessor { protected readonly options: IOptions, protected readonly context: IFileContext ) { + validateParams( + params, + ['tag'], + 'Unknown error: a tag param is not specified' + ); + const { className, slug } = getClassNameAndSlug( this.displayName, this.idx, @@ -68,43 +77,16 @@ export default abstract class BaseProcessor { this.className = className; this.slug = slug; - } - public get template(): (TemplateElement | ExpressionValue)[] { - return this.params.find(isTemplateParam)?.[1] ?? []; + [[, this.tag]] = params; } - public build(values: ValueCache): Artifact[] { - if (this.artifacts.length > 0) { - // FIXME: why it was called twice? - throw new Error('Tag is already built'); - } - - const artifact = templateProcessor(this, this.template, values); - if (artifact) { - this.artifacts.push(['css', artifact]); - } - - return this.artifacts; - } + public abstract build(values: ValueCache): void; public isValidValue(value: unknown): value is Value { return typeof value === 'function' || isCSSable(value) || hasMeta(value); } - /** - * It is called for each resolved expression in a template literal. - * @param node - * @param source - * @param unit - * @return chunk of CSS that should be added to extracted CSS - */ - public abstract addInterpolation( - node: Expression, - source: string, - unit?: string - ): string; - /** * Perform a replacement for the tag in evaluation time. * For example, `css` tag will be replaced with its className, @@ -121,23 +103,27 @@ export default abstract class BaseProcessor { */ public abstract doRuntimeReplacement(): void; - public abstract extractRules( - valueCache: ValueCache, - cssText: string, - loc?: SourceLocation | null - ): Rules; - /** * A replacement for tag referenced in a template literal. */ public abstract get asSelector(): string; - protected abstract get tagExpression(): Expression; - /** * A replacement for the tag in evaluation time. * For example, `css` tag will be replaced with its className, * whereas `styled` tag will be replaced with an object with metadata. */ public abstract get value(): Expression; + + protected tagSourceCode(): string { + if (this.tag.type === 'Identifier') { + return this.tag.name; + } + + return generator(this.tag).code; + } + + public toString(): string { + return this.tagSourceCode(); + } } diff --git a/packages/tags/src/TaggedTemplateProcessor.ts b/packages/tags/src/TaggedTemplateProcessor.ts new file mode 100644 index 000000000..e4a8aee37 --- /dev/null +++ b/packages/tags/src/TaggedTemplateProcessor.ts @@ -0,0 +1,65 @@ +import type { TemplateElement, Expression, SourceLocation } from '@babel/types'; + +import type { TailProcessorParams } from './BaseProcessor'; +import BaseProcessor from './BaseProcessor'; +import type { ExpressionValue, ValueCache, Rules, Params } from './types'; +import templateProcessor from './utils/templateProcessor'; +import { validateParams } from './utils/validateParams'; + +export default abstract class TaggedTemplateProcessor extends BaseProcessor { + #template: (TemplateElement | ExpressionValue)[]; + + public constructor(params: Params, ...args: TailProcessorParams) { + validateParams( + params, + ['tag', 'template'], + 'Invalid usage of template tag' + ); + const [tag, [, template]] = params; + + super([tag], ...args); + + template.forEach((element) => { + if ('kind' in element) { + this.dependencies.push(element); + } + }); + + this.#template = template; + } + + public override build(values: ValueCache) { + if (this.artifacts.length > 0) { + // FIXME: why it was called twice? + throw new Error('Tag is already built'); + } + + const artifact = templateProcessor(this, this.#template, values); + if (artifact) { + this.artifacts.push(['css', artifact]); + } + } + + /** + * It is called for each resolved expression in a template literal. + * @param node + * @param source + * @param unit + * @return chunk of CSS that should be added to extracted CSS + */ + public abstract addInterpolation( + node: Expression, + source: string, + unit?: string + ): string; + + public abstract extractRules( + valueCache: ValueCache, + cssText: string, + loc?: SourceLocation | null + ): Rules; + + public override toString(): string { + return `${super.toString()}\`…\``; + } +} diff --git a/packages/tags/src/index.ts b/packages/tags/src/index.ts index 6f6005c0e..c7b82100b 100644 --- a/packages/tags/src/index.ts +++ b/packages/tags/src/index.ts @@ -2,5 +2,7 @@ export * from './BaseProcessor'; export * from './types'; export { default as isSerializable } from './utils/isSerializable'; export * from './utils/types'; +export * from './utils/validateParams'; export { default as BaseProcessor } from './BaseProcessor'; +export { default as TaggedTemplateProcessor } from './TaggedTemplateProcessor'; export { default as hasMeta } from './utils/hasMeta'; diff --git a/packages/tags/src/types.ts b/packages/tags/src/types.ts index a6136960e..06870cb63 100644 --- a/packages/tags/src/types.ts +++ b/packages/tags/src/types.ts @@ -1,4 +1,9 @@ -import type { Expression, Identifier, TemplateElement } from '@babel/types'; +import type { + Expression, + Identifier, + TemplateElement, + MemberExpression, +} from '@babel/types'; export type StyledMeta = { __linaria: { @@ -45,11 +50,6 @@ export type Location = { line: number; }; -export interface IPlaceholder { - id: string; - resolver: () => string; -} - export interface ICSSRule { atom?: boolean; className: string; @@ -69,12 +69,16 @@ export type WrappedNode = string | { node: Identifier; source: string }; export type Rules = Record; -export type CallParam = ['call', ...ExpressionValue[]]; -export type MemberParam = ['member', string]; -export type TemplateParam = ['template', (TemplateElement | ExpressionValue)[]]; +export type TagParam = readonly ['tag', Identifier | MemberExpression]; +export type CallParam = readonly ['call', ...ExpressionValue[]]; +export type MemberParam = readonly ['member', string]; +export type TemplateParam = readonly [ + 'template', + (TemplateElement | ExpressionValue)[] +]; -export type Param = CallParam | MemberParam | TemplateParam; -export type Params = Param[]; +export type Param = TagParam | CallParam | MemberParam | TemplateParam; +export type Params = readonly Param[]; export type BuildCodeFrameErrorFn = ( msg: string, @@ -86,11 +90,6 @@ export enum ValueType { FUNCTION, } -export type ComponentDependency = { - ex: Identifier; - source: string; -}; - export type LazyValue = { buildCodeFrameError: BuildCodeFrameErrorFn; ex: Identifier; diff --git a/packages/tags/src/utils/templateProcessor.ts b/packages/tags/src/utils/templateProcessor.ts index 9cebfffff..25a515580 100644 --- a/packages/tags/src/utils/templateProcessor.ts +++ b/packages/tags/src/utils/templateProcessor.ts @@ -6,7 +6,7 @@ import type { TemplateElement, SourceLocation } from '@babel/types'; -import type BaseProcessor from '../BaseProcessor'; +import type TaggedTemplateProcessor from '../TaggedTemplateProcessor'; import type { ExpressionValue, ValueCache, @@ -24,7 +24,7 @@ import { units } from './units'; const unitRegex = new RegExp(`^(?:${units.join('|')})\\b`); export default function templateProcessor( - tagProcessor: BaseProcessor, + tagProcessor: TaggedTemplateProcessor, [...template]: (TemplateElement | ExpressionValue)[], valueCache: ValueCache ): [rules: Rules, sourceMapReplacements: Replacements] | null { diff --git a/packages/tags/src/utils/validateParams.ts b/packages/tags/src/utils/validateParams.ts new file mode 100644 index 000000000..36fc639c5 --- /dev/null +++ b/packages/tags/src/utils/validateParams.ts @@ -0,0 +1,61 @@ +import type { Param, Params } from '../types'; + +type ParamName = Param[0]; +type ParamConstraint = ParamName | [...ParamName[]] | '*'; + +export type ParamConstraints = + | [...ParamConstraint[]] + | [...ParamConstraint[], '...']; + +type GetParamByName = T extends '*' + ? Param + : T extends (infer TNames extends ParamName)[] + ? Extract + : T extends ParamName + ? Extract + : never; + +export type MapParams< + TNames extends ParamConstraints, + TRes extends Param[] = [] +> = TNames extends [infer THead, ...infer TTail extends ParamConstraints] + ? THead extends '...' + ? [...TRes, ...Params] + : MapParams]> + : TRes; + +export function isValidParams( + params: Params, + constraints: T +): params is MapParams { + for (let i = 0; i < constraints.length; i++) { + const constraint = constraints[i]; + if (constraint === '...') { + return true; + } + + if (constraint === '*') { + if (params[i] === undefined) { + return false; + } + } else if (Array.isArray(constraint)) { + if (constraint.every((c) => c !== params[i]?.[0])) { + return false; + } + } else if (constraint !== params[i]?.[0]) { + return false; + } + } + + return true; +} + +export function validateParams( + params: Params, + constraints: T, + message: string +): asserts params is MapParams { + if (!isValidParams(params, constraints)) { + throw new Error(message); + } +} diff --git a/packages/testkit/package.json b/packages/testkit/package.json index cb30f3f06..1abec3bcf 100644 --- a/packages/testkit/package.json +++ b/packages/testkit/package.json @@ -22,6 +22,7 @@ "@babel/types": "^7.18.9", "@linaria/atomic": "workspace:^", "@linaria/core": "workspace:^", + "@linaria/griffel": "workspace:^", "@linaria/logger": "workspace:^", "@linaria/utils": "workspace:^", "@types/babel__core": "^7.1.19", diff --git a/packages/testkit/src/__snapshots__/babel.test.ts.snap b/packages/testkit/src/__snapshots__/babel.test.ts.snap index 14a505930..d8555ee3f 100644 --- a/packages/testkit/src/__snapshots__/babel.test.ts.snap +++ b/packages/testkit/src/__snapshots__/babel.test.ts.snap @@ -1854,6 +1854,32 @@ CSS: } } +Dependencies: NA + +`; + +exports[`strategy shaker should process griffel makeStyles 1`] = ` +"import { __styles as _styles } from \\"@griffel/react\\"; +export const useStyles = /*#__PURE__*/_styles({ + root: { + mc9l5x: \\"f22iagw\\", + Bi91k9c: \\"faf35ka\\", + Bb9khzn: \\"f17t1d3d\\", + Btk3f3y: \\"fh8e7tb\\" + } +}, { + d: [\\".f22iagw{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}\\", \\".fh8e7tb .foo:hover{color:green;}\\"], + h: [\\".faf35ka:hover{color:red;}\\"], + f: [\\".f17t1d3d:focus:hover{color:blue;}\\"] +});" +`; + +exports[`strategy shaker should process griffel makeStyles 2`] = ` + +CSS: + + + Dependencies: NA `; diff --git a/packages/testkit/src/babel.test.ts b/packages/testkit/src/babel.test.ts index bd17bf198..99f3ad3a7 100644 --- a/packages/testkit/src/babel.test.ts +++ b/packages/testkit/src/babel.test.ts @@ -2424,4 +2424,27 @@ describe('strategy shaker', () => { expect(code).toMatchSnapshot(); expect(metadata).toMatchSnapshot(); }); + + it('should process griffel makeStyles', async () => { + const { code, metadata } = await transform( + dedent` + import { makeStyles } from '@linaria/griffel'; + + export const useStyles = makeStyles({ + root: { + display: 'flex', + + ':hover': { color: 'red' }, + ':focus': { ':hover': { color: 'blue' } }, + + '& .foo': { ':hover': { color: 'green' } }, + }, + }); + `, + [evaluator] + ); + + expect(code).toMatchSnapshot(); + expect(metadata).toMatchSnapshot(); + }); }); diff --git a/packages/testkit/src/utils/getTagProcessor.test.ts b/packages/testkit/src/utils/getTagProcessor.test.ts index 7c8e8d527..be8bd0387 100644 --- a/packages/testkit/src/utils/getTagProcessor.test.ts +++ b/packages/testkit/src/utils/getTagProcessor.test.ts @@ -1,7 +1,6 @@ import { join } from 'path'; import { parseSync } from '@babel/core'; -import generator from '@babel/generator'; import traverse from '@babel/traverse'; import dedent from 'dedent'; @@ -35,8 +34,7 @@ const run = (code: string): BaseProcessor | null => { function tagSource(processor: BaseProcessor | null): string | undefined { if (!processor) return undefined; - // @ts-expect-error tagExpression is protected field - return generator(processor.tagExpression).code; + return processor.toString(); } describe('getTagProcessor', () => { @@ -52,7 +50,7 @@ describe('getTagProcessor', () => { ` ); - expect(tagSource(result)).toBe('renamedStyled(_exp())'); + expect(tagSource(result)).toBe('renamedStyled(Cmp)`…`'); }); it('imported component', () => { @@ -65,7 +63,7 @@ describe('getTagProcessor', () => { ` ); - expect(tagSource(result)).toBe('styled(_exp())'); + expect(tagSource(result)).toBe('styled(Layout)`…`'); }); it('renamedStyled(Cmp)``', () => { @@ -79,7 +77,7 @@ describe('getTagProcessor', () => { ` ); - expect(tagSource(result)).toBe('renamedStyled(_exp())'); + expect(tagSource(result)).toBe('renamedStyled(Cmp)`…`'); }); it('(0, react_1.styled)(Cmp)``', () => { @@ -93,7 +91,7 @@ describe('getTagProcessor', () => { ` ); - expect(tagSource(result)).toBe('react_1.styled(_exp())'); + expect(tagSource(result)).toBe('react_1.styled(Cmp)`…`'); }); it('styled(Cmp)``', () => { @@ -107,7 +105,7 @@ describe('getTagProcessor', () => { ` ); - expect(tagSource(result)).toBe('styled(_exp())'); + expect(tagSource(result)).toBe('styled(Cmp)`…`'); }); it('styled(hoc(Title))``', () => { @@ -128,7 +126,7 @@ describe('getTagProcessor', () => { ` ); - expect(tagSource(result)).toBe('styled(_exp())'); + expect(tagSource(result)).toBe('styled(hoc(Title))`…`'); }); it('styled(() => { someLogic(); })``', () => { @@ -145,7 +143,7 @@ describe('getTagProcessor', () => { ` ); - expect(tagSource(result)).toBe('styled(() => {})'); + expect(tagSource(result)).toBe('styled(() => {…})`…`'); }); it('renamedStyled.div``', () => { @@ -157,7 +155,7 @@ describe('getTagProcessor', () => { ` ); - expect(tagSource(result)).toBe("renamedStyled('div')"); + expect(tagSource(result)).toBe("renamedStyled('div')`…`"); }); it('(0, react_1.styled.div)``', () => { @@ -169,7 +167,7 @@ describe('getTagProcessor', () => { ` ); - expect(tagSource(result)).toBe("react_1.styled('div')"); + expect(tagSource(result)).toBe("react_1.styled('div')`…`"); }); it('styled.div``', () => { @@ -181,7 +179,7 @@ describe('getTagProcessor', () => { ` ); - expect(tagSource(result)).toBe("styled('div')"); + expect(tagSource(result)).toBe("styled('div')`…`"); }); it('styled("div")``', () => { @@ -193,7 +191,7 @@ describe('getTagProcessor', () => { ` ); - expect(tagSource(result)).toBe('styled(_exp())'); + expect(tagSource(result)).toBe("styled('div')`…`"); }); it('(0, core_1.css)``', () => { @@ -205,7 +203,7 @@ describe('getTagProcessor', () => { ` ); - expect(tagSource(result)).toBe('core_1.css'); + expect(tagSource(result)).toBe('core_1.css`…`'); }); it('css``', () => { @@ -217,7 +215,7 @@ describe('getTagProcessor', () => { ` ); - expect(tagSource(result)).toBe('css'); + expect(tagSource(result)).toBe('css`…`'); }); it('atomic css``', () => { @@ -229,7 +227,7 @@ describe('getTagProcessor', () => { ` ); - expect(tagSource(result)).toBe('css'); + expect(tagSource(result)).toBe('css`…`'); }); it('re-imported css', () => { @@ -241,7 +239,7 @@ describe('getTagProcessor', () => { ` ); - expect(tagSource(result)).toBe('css'); + expect(tagSource(result)).toBe('css`…`'); }); it('re-imported styled', () => { @@ -253,7 +251,7 @@ describe('getTagProcessor', () => { ` ); - expect(tagSource(result)).toBe("styled('div')"); + expect(tagSource(result)).toBe("styled('div')`…`"); }); it('import from unknown package', () => { @@ -276,7 +274,7 @@ describe('getTagProcessor', () => { ` ); - expect(tagSource(result)).toBe("renamedStyled('div')"); + expect(tagSource(result)).toBe("renamedStyled('div')`…`"); }); it('require and destructing', () => { @@ -287,7 +285,7 @@ describe('getTagProcessor', () => { ` ); - expect(tagSource(result)).toBe("styled('div')"); + expect(tagSource(result)).toBe("styled('div')`…`"); }); describe('invalid usage', () => { @@ -297,7 +295,7 @@ describe('getTagProcessor', () => { dedent`import { css } from "@linaria/core"; export const square = css.div\`\`;` ); - expect(runner).toThrow('Invalid usage of `css` tag'); + expect(runner).toThrow('Invalid usage of template tag'); }); it('css("div")``', () => { @@ -306,7 +304,7 @@ describe('getTagProcessor', () => { dedent`import { css } from "@linaria/core"; export const square = css("div")\`\`;` ); - expect(runner).toThrow('Invalid usage of `css` tag'); + expect(runner).toThrow('Invalid usage of template tag'); }); it('styled`` tag', () => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 55cd69bfb..94ba2d42a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,8 +19,8 @@ importers: '@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 @@ -40,7 +40,7 @@ importers: git-raw-commits: ^2.0.3 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 @@ -59,8 +59,8 @@ importers: '@changesets/cli': 2.22.0 '@commitlint/config-conventional': 8.3.6 '@types/jest': 28.1.0 - '@typescript-eslint/eslint-plugin': 5.27.0_523ueztkooxdrjszuewjoeoedq - '@typescript-eslint/parser': 5.27.0_vjep2yp2sits3sqnodefgcbnfi + '@typescript-eslint/eslint-plugin': 5.30.7_7bimhicebavk6fj2dtw4lk7sqe + '@typescript-eslint/parser': 5.30.7_vjep2yp2sits3sqnodefgcbnfi all-contributors-cli: 6.20.0 babel-jest: 28.1.0_@babel+core@7.18.9 codecov: 3.8.3 @@ -70,16 +70,16 @@ importers: dtslint: 4.2.1_typescript@4.7.4 eslint: 8.16.0 eslint-config-airbnb: 19.0.4_dwfgiixbwetqupsqmiuv57rzoi - eslint-config-airbnb-typescript: 17.0.0_epouuwgtjme5oszue63gdonk5m + eslint-config-airbnb-typescript: 17.0.0_2hiyrae7qweshky5h3gt7obpny eslint-config-prettier: 8.5.0_eslint@8.16.0 - eslint-plugin-import: 2.26.0_xsmuhwqsfrjm7m3kqio7zoeziq + eslint-plugin-import: 2.26.0_bkngvi3wcdllq54uhhavdagiee eslint-plugin-jsx-a11y: 6.5.1_eslint@8.16.0 - eslint-plugin-prettier: 4.0.0_j7rsahgqtkecno6yauhsgsglf4 + eslint-plugin-prettier: 4.0.0_6edyqcvmz3ta2ebn7rq2nny6ci eslint-plugin-react: 7.30.0_eslint@8.16.0 eslint-plugin-react-hooks: 4.5.0_eslint@8.16.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 @@ -198,6 +198,7 @@ importers: specifiers: '@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 @@ -226,6 +227,7 @@ importers: 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 @@ -319,6 +321,23 @@ importers: packages/extractor: specifiers: {} + packages/griffel: + specifiers: + '@babel/types': ^7.18.9 + '@griffel/core': ^1.5.0 + '@linaria/logger': workspace:^ + '@linaria/tags': workspace:^ + '@linaria/utils': workspace:^ + ts-invariant: ^0.10.3 + dependencies: + '@griffel/core': 1.5.0 + '@linaria/logger': link:../logger + '@linaria/tags': link:../tags + '@linaria/utils': link:../utils + ts-invariant: 0.10.3 + devDependencies: + '@babel/types': 7.18.9 + packages/interop: specifiers: '@babel/core': ^7.18.9 @@ -419,13 +438,13 @@ importers: '@types/dedent': ^0.7.0 dedent: ^0.7.0 postcss: ^8.3.11 - prettier: ^2.6.2 + prettier: ^2.7.1 dependencies: postcss: 8.4.14 devDependencies: '@types/dedent': 0.7.0 dedent: 0.7.0 - prettier: 2.6.2 + prettier: 2.7.1 packages/shaker: specifiers: @@ -489,20 +508,24 @@ importers: packages/tags: specifiers: + '@babel/generator': ^7.18.9 '@babel/traverse': ^7.18.9 '@babel/types': ^7.18.9 '@linaria/logger': workspace:^ '@linaria/utils': workspace:^ '@types/babel__core': ^7.1.19 + '@types/babel__generator': ^7.6.4 '@types/babel__traverse': ^7.17.1 '@types/node': ^17.0.39 dependencies: + '@babel/generator': 7.18.9 '@linaria/logger': link:../logger '@linaria/utils': link:../utils devDependencies: '@babel/traverse': 7.18.9 '@babel/types': 7.18.9 '@types/babel__core': 7.1.19 + '@types/babel__generator': 7.6.4 '@types/babel__traverse': 7.17.1 '@types/node': 17.0.39 @@ -518,6 +541,7 @@ importers: '@linaria/babel-preset': workspace:^ '@linaria/core': workspace:^ '@linaria/extractor': workspace:^ + '@linaria/griffel': workspace:^ '@linaria/logger': workspace:^ '@linaria/react': workspace:^ '@linaria/shaker': workspace:^ @@ -555,6 +579,7 @@ importers: '@babel/types': 7.18.9 '@linaria/atomic': link:../atomic '@linaria/core': link:../core + '@linaria/griffel': link:../griffel '@linaria/logger': link:../logger '@linaria/utils': link:../utils '@types/babel__core': 7.1.19 @@ -1066,7 +1091,7 @@ packages: resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.18.8 + '@babel/types': 7.18.9 /@babel/helper-module-transforms/7.18.0: resolution: {integrity: sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA==} @@ -2733,6 +2758,10 @@ packages: engines: {node: '>=10.0.0'} dev: true + /@emotion/hash/0.8.0: + resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} + dev: false + /@emotion/is-prop-valid/0.8.8: resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==} dependencies: @@ -2760,6 +2789,16 @@ packages: - supports-color dev: true + /@griffel/core/1.5.0: + resolution: {integrity: sha512-NC0J3k4qxQq7fLicrlbbPIC85Gw7LgTUm0c9SWGRv7dsj1tXE6y2+eHAhJwA5230KkUvACPgwJRCaBikFxv+4Q==} + dependencies: + '@emotion/hash': 0.8.0 + csstype: 3.1.0 + rtl-css-js: 1.15.0 + stylis: 4.1.1 + tslib: 2.4.0 + dev: false + /@humanwhocodes/config-array/0.9.5: resolution: {integrity: sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==} engines: {node: '>=10.10.0'} @@ -3627,8 +3666,8 @@ packages: '@types/yargs-parser': 21.0.0 dev: true - /@typescript-eslint/eslint-plugin/5.27.0_523ueztkooxdrjszuewjoeoedq: - resolution: {integrity: sha512-DDrIA7GXtmHXr1VCcx9HivA39eprYBIFxbQEHI6NyraRDxCGpxAFiYQAT/1Y0vh1C+o2vfBiy4IuPoXxtTZCAQ==} + /@typescript-eslint/eslint-plugin/5.30.7_7bimhicebavk6fj2dtw4lk7sqe: + resolution: {integrity: sha512-l4L6Do+tfeM2OK0GJsU7TUcM/1oN/N25xHm3Jb4z3OiDU4Lj8dIuxX9LpVMS9riSXQs42D1ieX7b85/r16H9Fw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 @@ -3638,10 +3677,10 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.27.0_vjep2yp2sits3sqnodefgcbnfi - '@typescript-eslint/scope-manager': 5.27.0 - '@typescript-eslint/type-utils': 5.27.0_vjep2yp2sits3sqnodefgcbnfi - '@typescript-eslint/utils': 5.27.0_vjep2yp2sits3sqnodefgcbnfi + '@typescript-eslint/parser': 5.30.7_vjep2yp2sits3sqnodefgcbnfi + '@typescript-eslint/scope-manager': 5.30.7 + '@typescript-eslint/type-utils': 5.30.7_vjep2yp2sits3sqnodefgcbnfi + '@typescript-eslint/utils': 5.30.7_vjep2yp2sits3sqnodefgcbnfi debug: 4.3.4 eslint: 8.16.0 functional-red-black-tree: 1.0.1 @@ -3654,8 +3693,8 @@ packages: - supports-color dev: true - /@typescript-eslint/parser/5.27.0_vjep2yp2sits3sqnodefgcbnfi: - resolution: {integrity: sha512-8oGjQF46c52l7fMiPPvX4It3u3V3JipssqDfHQ2hcR0AeR8Zge+OYyKUCm5b70X72N1qXt0qgHenwN6Gc2SXZA==} + /@typescript-eslint/parser/5.30.7_vjep2yp2sits3sqnodefgcbnfi: + resolution: {integrity: sha512-Rg5xwznHWWSy7v2o0cdho6n+xLhK2gntImp0rJroVVFkcYFYQ8C8UJTSuTw/3CnExBmPjycjmUJkxVmjXsld6A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -3664,9 +3703,9 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 5.27.0 - '@typescript-eslint/types': 5.27.0 - '@typescript-eslint/typescript-estree': 5.27.0_typescript@4.7.4 + '@typescript-eslint/scope-manager': 5.30.7 + '@typescript-eslint/types': 5.30.7 + '@typescript-eslint/typescript-estree': 5.30.7_typescript@4.7.4 debug: 4.3.4 eslint: 8.16.0 typescript: 4.7.4 @@ -3674,16 +3713,16 @@ packages: - supports-color dev: true - /@typescript-eslint/scope-manager/5.27.0: - resolution: {integrity: sha512-VnykheBQ/sHd1Vt0LJ1JLrMH1GzHO+SzX6VTXuStISIsvRiurue/eRkTqSrG0CexHQgKG8shyJfR4o5VYioB9g==} + /@typescript-eslint/scope-manager/5.30.7: + resolution: {integrity: sha512-7BM1bwvdF1UUvt+b9smhqdc/eniOnCKxQT/kj3oXtj3LqnTWCAM0qHRHfyzCzhEfWX0zrW7KqXXeE4DlchZBKw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.27.0 - '@typescript-eslint/visitor-keys': 5.27.0 + '@typescript-eslint/types': 5.30.7 + '@typescript-eslint/visitor-keys': 5.30.7 dev: true - /@typescript-eslint/type-utils/5.27.0_vjep2yp2sits3sqnodefgcbnfi: - resolution: {integrity: sha512-vpTvRRchaf628Hb/Xzfek+85o//zEUotr1SmexKvTfs7czXfYjXVT/a5yDbpzLBX1rhbqxjDdr1Gyo0x1Fc64g==} + /@typescript-eslint/type-utils/5.30.7_vjep2yp2sits3sqnodefgcbnfi: + resolution: {integrity: sha512-nD5qAE2aJX/YLyKMvOU5jvJyku4QN5XBVsoTynFrjQZaDgDV6i7QHFiYCx10wvn7hFvfuqIRNBtsgaLe0DbWhw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -3692,7 +3731,7 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/utils': 5.27.0_vjep2yp2sits3sqnodefgcbnfi + '@typescript-eslint/utils': 5.30.7_vjep2yp2sits3sqnodefgcbnfi debug: 4.3.4 eslint: 8.16.0 tsutils: 3.21.0_typescript@4.7.4 @@ -3701,13 +3740,13 @@ packages: - supports-color dev: true - /@typescript-eslint/types/5.27.0: - resolution: {integrity: sha512-lY6C7oGm9a/GWhmUDOs3xAVRz4ty/XKlQ2fOLr8GAIryGn0+UBOoJDWyHer3UgrHkenorwvBnphhP+zPmzmw0A==} + /@typescript-eslint/types/5.30.7: + resolution: {integrity: sha512-ocVkETUs82+U+HowkovV6uxf1AnVRKCmDRNUBUUo46/5SQv1owC/EBFkiu4MOHeZqhKz2ktZ3kvJJ1uFqQ8QPg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree/5.27.0_typescript@4.7.4: - resolution: {integrity: sha512-QywPMFvgZ+MHSLRofLI7BDL+UczFFHyj0vF5ibeChDAJgdTV8k4xgEwF0geFhVlPc1p8r70eYewzpo6ps+9LJQ==} + /@typescript-eslint/typescript-estree/5.30.7_typescript@4.7.4: + resolution: {integrity: sha512-tNslqXI1ZdmXXrHER83TJ8OTYl4epUzJC0aj2i4DMDT4iU+UqLT3EJeGQvJ17BMbm31x5scSwo3hPM0nqQ1AEA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -3715,8 +3754,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 5.27.0 - '@typescript-eslint/visitor-keys': 5.27.0 + '@typescript-eslint/types': 5.30.7 + '@typescript-eslint/visitor-keys': 5.30.7 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 @@ -3727,16 +3766,16 @@ packages: - supports-color dev: true - /@typescript-eslint/utils/5.27.0_vjep2yp2sits3sqnodefgcbnfi: - resolution: {integrity: sha512-nZvCrkIJppym7cIbP3pOwIkAefXOmfGPnCM0LQfzNaKxJHI6VjI8NC662uoiPlaf5f6ymkTy9C3NQXev2mdXmA==} + /@typescript-eslint/utils/5.30.7_vjep2yp2sits3sqnodefgcbnfi: + resolution: {integrity: sha512-Z3pHdbFw+ftZiGUnm1GZhkJgVqsDL5CYW2yj+TB2mfXDFOMqtbzQi2dNJIyPqPbx9mv2kUxS1gU+r2gKlKi1rQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: '@types/json-schema': 7.0.11 - '@typescript-eslint/scope-manager': 5.27.0 - '@typescript-eslint/types': 5.27.0 - '@typescript-eslint/typescript-estree': 5.27.0_typescript@4.7.4 + '@typescript-eslint/scope-manager': 5.30.7 + '@typescript-eslint/types': 5.30.7 + '@typescript-eslint/typescript-estree': 5.30.7_typescript@4.7.4 eslint: 8.16.0 eslint-scope: 5.1.1 eslint-utils: 3.0.0_eslint@8.16.0 @@ -3745,11 +3784,11 @@ packages: - typescript dev: true - /@typescript-eslint/visitor-keys/5.27.0: - resolution: {integrity: sha512-46cYrteA2MrIAjv9ai44OQDUoCZyHeGIc4lsjCUX2WT6r4C+kidz1bNiR4017wHOPUythYeH+Sc7/cFP97KEAA==} + /@typescript-eslint/visitor-keys/5.30.7: + resolution: {integrity: sha512-KrRXf8nnjvcpxDFOKej4xkD7657+PClJs5cJVSG7NNoCNnjEdc46juNAQt7AyuWctuCgs6mVRc1xGctEqrjxWw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.27.0 + '@typescript-eslint/types': 5.30.7 eslint-visitor-keys: 3.3.0 dev: true @@ -5239,7 +5278,6 @@ packages: /csstype/3.1.0: resolution: {integrity: sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==} - dev: true /csv-generate/3.4.3: resolution: {integrity: sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==} @@ -5947,13 +5985,13 @@ packages: dependencies: confusing-browser-globals: 1.0.11 eslint: 8.16.0 - eslint-plugin-import: 2.26.0_xsmuhwqsfrjm7m3kqio7zoeziq + eslint-plugin-import: 2.26.0_bkngvi3wcdllq54uhhavdagiee object.assign: 4.1.2 object.entries: 1.1.5 semver: 6.3.0 dev: true - /eslint-config-airbnb-typescript/17.0.0_epouuwgtjme5oszue63gdonk5m: + /eslint-config-airbnb-typescript/17.0.0_2hiyrae7qweshky5h3gt7obpny: resolution: {integrity: sha512-elNiuzD0kPAPTXjFWg+lE24nMdHMtuxgYoD30OyMD6yrW1AhFZPAg27VX7d3tzOErw+dgJTNWfRSDqEcXb4V0g==} peerDependencies: '@typescript-eslint/eslint-plugin': ^5.13.0 @@ -5961,11 +5999,11 @@ packages: eslint: ^7.32.0 || ^8.2.0 eslint-plugin-import: ^2.25.3 dependencies: - '@typescript-eslint/eslint-plugin': 5.27.0_523ueztkooxdrjszuewjoeoedq - '@typescript-eslint/parser': 5.27.0_vjep2yp2sits3sqnodefgcbnfi + '@typescript-eslint/eslint-plugin': 5.30.7_7bimhicebavk6fj2dtw4lk7sqe + '@typescript-eslint/parser': 5.30.7_vjep2yp2sits3sqnodefgcbnfi eslint: 8.16.0 eslint-config-airbnb-base: 15.0.0_btspkuwbqkl4adpiufzbathtpi - eslint-plugin-import: 2.26.0_xsmuhwqsfrjm7m3kqio7zoeziq + eslint-plugin-import: 2.26.0_bkngvi3wcdllq54uhhavdagiee dev: true /eslint-config-airbnb/19.0.4_dwfgiixbwetqupsqmiuv57rzoi: @@ -5980,7 +6018,7 @@ packages: dependencies: eslint: 8.16.0 eslint-config-airbnb-base: 15.0.0_btspkuwbqkl4adpiufzbathtpi - eslint-plugin-import: 2.26.0_xsmuhwqsfrjm7m3kqio7zoeziq + eslint-plugin-import: 2.26.0_bkngvi3wcdllq54uhhavdagiee eslint-plugin-jsx-a11y: 6.5.1_eslint@8.16.0 eslint-plugin-react: 7.30.0_eslint@8.16.0 eslint-plugin-react-hooks: 4.5.0_eslint@8.16.0 @@ -6006,7 +6044,7 @@ packages: - supports-color dev: true - /eslint-module-utils/2.7.3_nd4nb6nccnlbwilvit6hlaep3q: + /eslint-module-utils/2.7.3_f7rggfnq2itgt2uu6gnhkpzzne: resolution: {integrity: sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==} engines: {node: '>=4'} peerDependencies: @@ -6024,7 +6062,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.27.0_vjep2yp2sits3sqnodefgcbnfi + '@typescript-eslint/parser': 5.30.7_vjep2yp2sits3sqnodefgcbnfi debug: 3.2.7 eslint-import-resolver-node: 0.3.6 find-up: 2.1.0 @@ -6032,7 +6070,7 @@ packages: - supports-color dev: true - /eslint-plugin-import/2.26.0_xsmuhwqsfrjm7m3kqio7zoeziq: + /eslint-plugin-import/2.26.0_bkngvi3wcdllq54uhhavdagiee: resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==} engines: {node: '>=4'} peerDependencies: @@ -6042,14 +6080,14 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.27.0_vjep2yp2sits3sqnodefgcbnfi + '@typescript-eslint/parser': 5.30.7_vjep2yp2sits3sqnodefgcbnfi array-includes: 3.1.5 array.prototype.flat: 1.3.0 debug: 2.6.9 doctrine: 2.1.0 eslint: 8.16.0 eslint-import-resolver-node: 0.3.6 - eslint-module-utils: 2.7.3_nd4nb6nccnlbwilvit6hlaep3q + eslint-module-utils: 2.7.3_f7rggfnq2itgt2uu6gnhkpzzne has: 1.0.3 is-core-module: 2.9.0 is-glob: 4.0.3 @@ -6084,7 +6122,7 @@ packages: minimatch: 3.1.2 dev: true - /eslint-plugin-prettier/4.0.0_j7rsahgqtkecno6yauhsgsglf4: + /eslint-plugin-prettier/4.0.0_6edyqcvmz3ta2ebn7rq2nny6ci: resolution: {integrity: sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==} engines: {node: '>=6.0.0'} peerDependencies: @@ -6097,7 +6135,7 @@ packages: dependencies: eslint: 8.16.0 eslint-config-prettier: 8.5.0_eslint@8.16.0 - prettier: 2.6.2 + prettier: 2.7.1 prettier-linter-helpers: 1.0.0 dev: true @@ -8154,7 +8192,7 @@ packages: '@babel/generator': 7.18.2 '@babel/plugin-syntax-typescript': 7.17.12_@babel+core@7.18.9 '@babel/traverse': 7.18.2 - '@babel/types': 7.18.4 + '@babel/types': 7.18.9 '@jest/expect-utils': 28.1.0 '@jest/transform': 28.1.0 '@jest/types': 28.1.0 @@ -9774,8 +9812,8 @@ packages: hasBin: true dev: true - /prettier/2.6.2: - resolution: {integrity: sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==} + /prettier/2.7.1: + resolution: {integrity: sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==} engines: {node: '>=10.13.0'} hasBin: true dev: true @@ -10397,6 +10435,12 @@ packages: fsevents: 2.3.2 dev: true + /rtl-css-js/1.15.0: + resolution: {integrity: sha512-99Cu4wNNIhrI10xxUaABHsdDqzalrSRTie4GeCmbGVuehm4oj+fIy8fTzB+16pmKe8Bv9rl+hxIBez6KxExTew==} + dependencies: + '@babel/runtime': 7.18.3 + dev: false + /run-async/2.4.1: resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} engines: {node: '>=0.12.0'} @@ -11052,6 +11096,10 @@ packages: resolution: {integrity: sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q==} dev: false + /stylis/4.1.1: + resolution: {integrity: sha512-lVrM/bNdhVX2OgBFNa2YJ9Lxj7kPzylieHd3TNjuGE0Re9JB7joL5VUKOVH1kdNNJTgGPpT8hmwIAPLaSyEVFQ==} + dev: false + /sugarss/2.0.0: resolution: {integrity: sha512-WfxjozUk0UVA4jm+U1d736AUpzSrNsQcIbyOkoE364GrtWmIrFdk5lksEupgWMD4VaT/0kVx1dobpiDumSgmJQ==} dependencies: