Skip to content

Commit

Permalink
fix: use transform-tagged-template-literal plugin explicitly (#609)
Browse files Browse the repository at this point in the history
  • Loading branch information
jayu committed May 15, 2020
1 parent 8abb51d commit df7cd17
Show file tree
Hide file tree
Showing 12 changed files with 94 additions and 49 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-modules-commonjs": "^7.9.0",
"@babel/plugin-transform-runtime": "^7.9.0",
"@babel/plugin-transform-template-literals": "^7.8.3",
"@babel/register": "^7.9.0",
"@babel/template": "^7.8.6",
"@babel/traverse": "^7.9.0",
Expand Down Expand Up @@ -138,4 +139,4 @@
"<rootDir>/jest/resolveTypescript.js"
]
}
}
}
2 changes: 1 addition & 1 deletion src/__tests__/__snapshots__/preval.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ CSS:
;
}
Dependencies: @babel/runtime/helpers/interopRequireDefault, @babel/runtime/helpers/taggedTemplateLiteralLoose
Dependencies: @babel/runtime/helpers/interopRequireDefault, @babel/runtime/helpers/taggedTemplateLiteral
`;
Expand Down
2 changes: 1 addition & 1 deletion src/babel/evaluators/buildOptions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* This file handles preparing babel config for Linaria babel plugin.
* This file handles preparing babel config for Linaria preevaluation.
*/

import { PluginItem, TransformOptions } from '@babel/core';
Expand Down
12 changes: 12 additions & 0 deletions src/babel/evaluators/extractor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ const extractor: Evaluator = (filename, options, text, only = null) => {
{ useESModules: false },
]);

// We made a mistake somewhen, and linaria preval was dependent on `plugin-transform-template-literals`
// Usually it was loaded into preval, because user was using `@babel/preset-env` preset which included that plugin. Internally we used this preset for tests (and previously for everything) - thats why we implemented behavior based on existing of that plugin
// The ordering is very important here, that's why it is added as a preset, not just as a plugin. It makes this plugin run *AFTER* linaria preset, which is required to make have the current behavior.
// In preval we have 2 visitors, one for Call Expressions and second for TaggedTemplateLiterals. Babel process TaggedTemplates first for some reason, and we grab only the css`` statements, we skip styled statements at this stage.
// Then it process TaggedTemplateLiterals with mentioned plugin, which transforms them to CallExpressions (babel seems to apply thw whole set of plugins for particular visitor, then for the next visitor and so on).
// Then Linaria can identify all `styled` as call expressions, including `styled.h1`, `styled.p` and others.

// Presets ordering is from last to first, so we add the plugin at the beginning of the list, which persist the order that was established with formerly used `@babel/preset-env`.

transformOptions.presets!.unshift({
plugins: ['@babel/plugin-transform-template-literals'],
});
// Expressions will be extracted only for __linariaPreval.
// In all other cases a code will be returned as is.
let { code } = transformSync(text, transformOptions)!;
Expand Down
15 changes: 7 additions & 8 deletions src/babel/evaluators/preeval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
import { NodePath } from '@babel/traverse';
import { types } from '@babel/core';
import GenerateClassNames from '../visitors/GenerateClassNames';
import JSXElement from '../visitors/JSXElement';
import CallExpression from '../visitors/CallExpression';
import DetectStyledImportName from '../visitors/DetectStyledImportName';
import JSXElement from './visitors/JSXElement';
import ProcessStyled from './visitors/ProcessStyled';
import ProcessCSS from './visitors/ProcessCSS';
import { State, StrictOptions } from '../types';
import CSSTemplateExpression from '../visitors/CSSTemplateExpression';
import ImportDeclaration from '../visitors/ImportDeclaration';

function preeval(_babel: any, options: StrictOptions) {
return {
Expand All @@ -26,16 +26,15 @@ function preeval(_babel: any, options: StrictOptions) {
// We need our transforms to run before anything else
// So we traverse here instead of a in a visitor
path.traverse({
ImportDeclaration: p => ImportDeclaration(p, state),
ImportDeclaration: p => DetectStyledImportName(p, state),
TaggedTemplateExpression: p =>
GenerateClassNames(p, state, options),

JSXElement,
});
},
},
CallExpression,
TaggedTemplateExpression: CSSTemplateExpression,
CallExpression: ProcessStyled,
TaggedTemplateExpression: ProcessCSS, // TaggedTemplateExpression is processed before CallExpression
},
};
}
Expand Down
6 changes: 5 additions & 1 deletion src/babel/evaluators/shaker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ function prepareForShake(
transformOptions.ast = true;
transformOptions.presets!.unshift([
'@babel/preset-env',
{ targets: 'ie 11' },
{
targets: 'ie 11',
// we need this plugin so we list it explicitly, explanation in `evaluators/extractor/index`
include: ['@babel/plugin-transform-template-literals'],
},
]);
transformOptions.presets!.unshift([require.resolve('../preeval'), options]);
transformOptions.plugins!.unshift('transform-react-remove-prop-types');
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
/**
* This visitor replaces css tag with the generated className
*
*/

import { NodePath } from '@babel/traverse';
import { types as t } from '@babel/core';
import getLinariaComment from '../utils/getLinariaComment';
import getLinariaComment from '../../utils/getLinariaComment';

export default function CSSTemplateExpression(
path: NodePath<t.TaggedTemplateExpression>
) {
export default function ProcessCSS(path: NodePath<t.TaggedTemplateExpression>) {
if (t.isIdentifier(path.node.tag) && path.node.tag.name === 'css') {
const [, , className] = getLinariaComment(path);
if (!className) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
/**
* 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 { NodePath } from '@babel/traverse';
import { types } from '@babel/core';
import getLinariaComment from '../utils/getLinariaComment';
import getLinariaComment from '../../utils/getLinariaComment';
import { expression } from '@babel/template';

const linariaComponentTpl = expression(
Expand All @@ -13,7 +22,7 @@ const linariaComponentTpl = expression(
}`
);

export default function CallExpression(path: NodePath<types.CallExpression>) {
export default function ProcessStyled(path: NodePath<types.CallExpression>) {
const [, displayName, className] = getLinariaComment(path);
if (!className) {
return;
Expand Down
18 changes: 8 additions & 10 deletions src/babel/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ import {
ValueCache,
} from './types';
import CollectDependencies from './visitors/CollectDependencies';
import ImportDeclaration from './visitors/ImportDeclaration';
import { debug } from './utils/logger';
import DetectStyledImportName from './visitors/DetectStyledImportName';
import GenerateClassNames from './visitors/GenerateClassNames';
import { debug } from './utils/logger';

function isLazyValue(v: ExpressionValue): v is LazyValue {
return v.kind === ValueType.LAZY;
Expand Down Expand Up @@ -113,7 +113,7 @@ export default function extract(_babel: any, options: StrictOptions) {
// We need our transforms to run before anything else
// So we traverse here instead of a in a visitor
path.traverse({
ImportDeclaration: p => ImportDeclaration(p, state),
ImportDeclaration: p => DetectStyledImportName(p, state),
TaggedTemplateExpression: p => {
GenerateClassNames(p, state, options);
CollectDependencies(p, state, options);
Expand Down Expand Up @@ -186,13 +186,11 @@ export default function extract(_babel: any, options: StrictOptions) {
},
exit(_: any, state: State) {
if (Object.keys(state.rules).length) {
// Store the result as the file metadata
state.file.metadata = {
linaria: {
rules: state.rules,
replacements: state.replacements,
dependencies: state.dependencies,
},
// Store the result as the file metadata under linaria key
state.file.metadata.linaria = {
rules: state.rules,
replacements: state.replacements,
dependencies: state.dependencies,
};
}

Expand Down
47 changes: 30 additions & 17 deletions src/babel/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,32 +56,45 @@ export type TemplateExpression = {
expressionValues: ExpressionValue[];
};

type Rules = {
[selector: string]: {
className: string;
displayName: string;
cssText: string;
start: Location | null | undefined;
};
};

type Replacements = Array<{
original: {
start: Location;
end: Location;
};
length: number;
}>;

type Dependencies = string[];

export type State = {
queue: TemplateExpression[];
rules: {
[selector: string]: {
className: string;
displayName: string;
cssText: string;
start: Location | null | undefined;
};
};
replacements: Array<{
original: {
start: Location;
end: Location;
};
length: number;
}>;
rules: Rules;
replacements: Replacements;
index: number;
dependencies: string[];
dependencies: Dependencies;
file: {
opts: {
cwd: string;
root: string;
filename: string;
};
metadata: any;
metadata: {
localName?: string;
linaria?: {
rules: Rules;
replacements: Replacements;
dependencies: Dependencies;
};
};
};
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
/* eslint-disable no-param-reassign */
/**
* This Visitor checks if import of `linaria/react` was renamed and stores that information in state
*/

import { types as t } from '@babel/core';
import { NodePath } from '@babel/traverse';
import { State } from '../types';

export default function ImportDeclaration(
export default function DetectStyledImportName(
path: NodePath<t.ImportDeclaration>,
state: State
) {
if (!t.isLiteral(path.node.source, { value: 'linaria/react' })) return;
if (!t.isLiteral(path.node.source, { value: 'linaria/react' })) {
return;
}

path.node.specifiers.forEach(specifier => {
if (!t.isImportSpecifier(specifier)) return;

if (!t.isImportSpecifier(specifier)) {
return;
}
if (specifier.local.name !== specifier.imported.name) {
state.file.metadata.localName = specifier.local.name;
}
Expand Down

0 comments on commit df7cd17

Please sign in to comment.