diff --git a/src/features/css-prop.ts b/src/features/css-prop.ts index bd59063a..ca397a47 100644 --- a/src/features/css-prop.ts +++ b/src/features/css-prop.ts @@ -16,6 +16,7 @@ import wrapInClass from '../utils/wrapInClass'; const HAS_JSX = Symbol('Astroturf has jsx'); const HAS_CREATE_ELEMENT = Symbol('Astroturf has createElement call'); +const IS_JSX = Symbol('Is a JSX call expression'); type CssPropPluginState = PluginState & { [JSX_IDENTS]: { @@ -31,6 +32,15 @@ const isCssPropTag = (tagPath: NodePath, options: ResolvedOptions) => ? isStylesheetTag(tagPath, options) : isCssTag(tagPath, options); +const isJsxCallExpression = (p: NodePath) => { + const result = + p.isCallExpression() && + // @ts-ignore + p.get('callee').referencesImport('react/jsx-runtime'); + + return result; +}; + export const isCreateElementCall = (p: NodePath) => p.isCallExpression() && (p.get('callee.property') as any).node && @@ -164,6 +174,14 @@ const cssPropertyVisitors = { state.processed = true; } }, + CallExpression(path: NodePath) { + // prevent the inner traversal from finding nested css props, on `children:` keys + // but mark the path so we can skip the check when the outer traversal finds it + if (isCreateElementCall(path) || isJsxCallExpression(path)) { + path[IS_JSX] = true; + path.skip(); + } + }, }; export default { @@ -193,7 +211,12 @@ export default { const { file } = state; const pluginOptions = state.defaultedOptions; - if (!isCreateElementCall(path)) return; + if ( + !path[IS_JSX] && + !isCreateElementCall(path) && + !isJsxCallExpression(path) + ) + return; const typeName = getNameFromPath(path.get('arguments')[0]); @@ -208,10 +231,12 @@ export default { }; // We aren't checking very hard that this is a React createElement call - if (propsPath) { - propsPath.traverse(cssPropertyVisitors, innerState); + if (!propsPath) { + return; } + propsPath.traverse(cssPropertyVisitors, innerState); + if (innerState.processed) { const { jsx } = state[JSX_IDENTS]; const { changeset } = file.get(STYLES); @@ -223,6 +248,7 @@ export default { end: callee.node.end, }); + // console.log('HERE', callee.node); callee.replaceWith(t.identifier(jsx.name)); file.set(HAS_CREATE_ELEMENT, true); } diff --git a/src/runtime/react.d.ts b/src/runtime/react.d.ts index f3b6ec36..1eb5e63a 100644 --- a/src/runtime/react.d.ts +++ b/src/runtime/react.d.ts @@ -139,12 +139,15 @@ export interface StyledOptions { export type mapper = (input: TInner) => TOuter; export interface CreateStyled extends StyledTags { - >( + >( component: C, options?: StyledOptions, ): StyledFunction; - , OtherProps extends object>( + < + C extends IntrinsicElementsKeys | React.ComponentType, + OtherProps extends object + >( component: C, options?: StyledOptions | undefined, ): StyledFunction; diff --git a/test/__file_snapshots__/integration-js.js b/test/__file_snapshots__/integration-js.js index 8d87ceee..7b1697b7 100644 --- a/test/__file_snapshots__/integration-js.js +++ b/test/__file_snapshots__/integration-js.js @@ -1,3 +1,4 @@ +"use strict"; (self["webpackChunk"] = self["webpackChunk"] || []).push([["main"],{ /***/ "./integration/Button.js": @@ -6,7 +7,6 @@ \*******************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "styles": () => (/* binding */ styles), @@ -33,7 +33,6 @@ const Button = /*#__PURE__*/(0,astroturf_react__WEBPACK_IMPORTED_MODULE_0__.defa \*****************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "MyComponent": () => (/* binding */ MyComponent), @@ -94,7 +93,6 @@ function MyComponent() { \********************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) @@ -117,7 +115,6 @@ const Widget = /*#__PURE__*/(0,astroturf_react__WEBPACK_IMPORTED_MODULE_0__.defa \************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) @@ -140,7 +137,6 @@ const Doodad = /*#__PURE__*/(0,astroturf_react__WEBPACK_IMPORTED_MODULE_0__.defa \****************************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) @@ -156,7 +152,6 @@ __webpack_require__.r(__webpack_exports__); \***********************************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) @@ -172,7 +167,6 @@ __webpack_require__.r(__webpack_exports__); \*******************************************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) @@ -188,7 +182,6 @@ __webpack_require__.r(__webpack_exports__); \*********************************************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) @@ -204,7 +197,6 @@ __webpack_require__.r(__webpack_exports__); \***************************************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) @@ -220,7 +212,6 @@ __webpack_require__.r(__webpack_exports__); \***********************************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) @@ -236,7 +227,6 @@ __webpack_require__.r(__webpack_exports__); \*******************************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) @@ -252,7 +242,6 @@ __webpack_require__.r(__webpack_exports__); \*****************************************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) @@ -268,7 +257,6 @@ __webpack_require__.r(__webpack_exports__); \****************************************************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) @@ -284,7 +272,6 @@ __webpack_require__.r(__webpack_exports__); \*******************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "stylesheet": () => (/* binding */ stylesheet), @@ -313,7 +300,6 @@ const css = () => { \*****************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "resolveVariants": () => (/* binding */ resolveVariants), @@ -370,7 +356,6 @@ jsx.F = react__WEBPACK_IMPORTED_MODULE_0__.Fragment; \*******************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__), @@ -499,9 +484,8 @@ function styled(type, options, settings) { }, /******/ __webpack_require__ => { // webpackRuntimeModules -/******/ "use strict"; -/******/ /******/ var __webpack_exec__ = (moduleId) => (__webpack_require__(__webpack_require__.s = moduleId)) -/******/ var __webpack_exports__ = __webpack_require__.O(undefined, ["vendors-node_modules_react_index_js"], () => (__webpack_exec__("./integration/main.js"))); +/******/ __webpack_require__.O(0, ["vendors-node_modules_react_index_js"], () => (__webpack_exec__("./integration/main.js"))); +/******/ var __webpack_exports__ = __webpack_require__.O(); /******/ } ]); \ No newline at end of file diff --git a/test/__file_snapshots__/issue-365-js.js b/test/__file_snapshots__/issue-365-js.js index e3d4d632..bba3c61c 100644 --- a/test/__file_snapshots__/issue-365-js.js +++ b/test/__file_snapshots__/issue-365-js.js @@ -1,3 +1,4 @@ +"use strict"; (self["webpackChunk"] = self["webpackChunk"] || []).push([["main"],{ /***/ "./integration/issue-365.js": @@ -6,7 +7,6 @@ \**********************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony import */ var astroturf_react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! astroturf/react */ "../src/runtime/react.js"); /* harmony import */ var issue_365_mixins_module_css_astroturf_inline_loader_style_Users_jquense_src_astroturf_test_integration_issue_365_js_mixins__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! issue-365-mixins.module.css!=!astroturf/inline-loader?style!./integration/issue-365.js?mixins */ "issue-365-mixins.module.css!=!../src/inline-loader.ts?style!./integration/issue-365.js?mixins"); @@ -31,7 +31,6 @@ const BlockStyled = /*#__PURE__*/(0,astroturf_react__WEBPACK_IMPORTED_MODULE_0__ \***************************************************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) @@ -47,7 +46,6 @@ __webpack_require__.r(__webpack_exports__); \*****************************************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) @@ -63,7 +61,6 @@ __webpack_require__.r(__webpack_exports__); \*******************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "stylesheet": () => (/* binding */ stylesheet), @@ -92,7 +89,6 @@ const css = () => { \*****************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "resolveVariants": () => (/* binding */ resolveVariants), @@ -149,7 +145,6 @@ jsx.F = react__WEBPACK_IMPORTED_MODULE_0__.Fragment; \*******************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__), @@ -278,9 +273,8 @@ function styled(type, options, settings) { }, /******/ __webpack_require__ => { // webpackRuntimeModules -/******/ "use strict"; -/******/ /******/ var __webpack_exec__ = (moduleId) => (__webpack_require__(__webpack_require__.s = moduleId)) -/******/ var __webpack_exports__ = __webpack_require__.O(undefined, ["vendors-node_modules_react_index_js"], () => (__webpack_exec__("./integration/issue-365.js"))); +/******/ __webpack_require__.O(0, ["vendors-node_modules_react_index_js"], () => (__webpack_exec__("./integration/issue-365.js"))); +/******/ var __webpack_exports__ = __webpack_require__.O(); /******/ } ]); \ No newline at end of file diff --git a/test/css-prop.test.js b/test/css-prop.test.js index 3c928421..519c5040 100644 --- a/test/css-prop.test.js +++ b/test/css-prop.test.js @@ -107,6 +107,7 @@ describe('css prop', () => { `, ); + // expect(code).toMatchSnapshot(); expect(code).not.toMatch('React.createElement'); expect(code).not.toContain('/** @jsx'); expect(code).not.toContain('/** @jsxFrag'); @@ -118,6 +119,41 @@ describe('css prop', () => { }, ); + testAllRunners( + 'should find when used with automatic runtime', + async (runner) => { + const [code, styles] = await runner( + ` + import { css } from 'astroturf'; + import { jsx as _jsx } from "react/jsx-runtime"; + import { jsxs as _jsxs } from "react/jsx-runtime"; + + function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } + + const Widget = React.forwardRef((props, ref) => { + return /*#__PURE__*/_jsxs('div', _extends({}, props, { + tabIndex: -1, + css: css\` + color: red + \`, + children: [/*#__PURE__*/_jsx('span', { css: 'width: 3rem' }), children] + })) + }) + `, + ); + + expect(code).not.toMatch('_jsxs('); + expect(code).not.toMatch('_jsx('); + expect(code).not.toContain('/** @jsx'); + expect(code).not.toContain('/** @jsxFrag'); + + expect(code).toMatch('import _j from "astroturf/jsx";'); + expect(code).toMatch('_j('); + expect(styles).toHaveLength(2); + expect(styles[0].identifier).toEqual('CssProp1_div'); + }, + ); + testAllRunners( 'should inject imports in the right order', async (runner, { requirePath }) => {