diff --git a/.gitignore b/.gitignore index 2b2ed17..7406a5a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ node_modules coverage generator-tsx-* -generators/**/*.js -generators/**/*.d.ts +generators/*.js +generators/*.d.ts +generators/*/*.js +generators/*/*.d.ts package .yo-rc.json diff --git a/.vscode/settings.json b/.vscode/settings.json index 912a8bd..7af7117 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,8 +9,10 @@ "**/.hg": true, "**/CVS": true, "**/.DS_Store": true, - "generators/**/*.js": true, - "generators/**/*.d.ts": true + "generators/*.js": true, + "generators/*.d.ts": true, + "generators/*/*.js": true, + "generators/*/*.d.ts": true }, "search.exclude": { "**/coverge": true, diff --git a/README.md b/README.md index bbe6d73..8d3ffce 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ See [Design Goals](https://github.com/adobe/generator-tsx/wiki/Design-Goals). - [Router](https://github.com/ReactTraining/react-router) - [Testing Library](https://github.com/testing-library/react-testing-library#react-testing-library) - [react-intl](https://github.com/formatjs/react-intl) +- CSS in JS via [Linaria](https://linaria.now.sh/) ## Installation diff --git a/generators/app/index.ts b/generators/app/index.ts index c886518..ba25ebd 100644 --- a/generators/app/index.ts +++ b/generators/app/index.ts @@ -85,6 +85,7 @@ export = class extends Generator { '.gitignore', '.markdownlint.json', '.travis.yml', + 'config', 'tsconfig.json', ].forEach(filename => { this.fs.copy( @@ -124,6 +125,7 @@ export = class extends Generator { ) this.npmInstall( [ + '@craco/craco@5', '@jedmao/tsconfig', '@types/fetch-mock@^7', '@types/jest@^24', @@ -137,9 +139,12 @@ export = class extends Generator { '@types/redux-logger@^3', '@types/redux-mock-store@^1', '@types/webpack-env@^1', + // TODO: https://github.com/callstack/linaria/issues/420 + 'core-js@2', 'fetch-mock@^7', 'husky@^2', 'jest-fetch-mock@^2', + 'linaria@1', 'lint-staged@^8', 'prettier@^1', 'react-scripts@^3', diff --git a/generators/app/templates/.gitignore b/generators/app/templates/.gitignore index 4d29575..f33ce71 100644 --- a/generators/app/templates/.gitignore +++ b/generators/app/templates/.gitignore @@ -21,3 +21,6 @@ npm-debug.log* yarn-debug.log* yarn-error.log* + +# linaria +src/.linaria_cache diff --git a/generators/app/templates/.vscode/settings.json b/generators/app/templates/.vscode/settings.json index 04b0553..908b357 100644 --- a/generators/app/templates/.vscode/settings.json +++ b/generators/app/templates/.vscode/settings.json @@ -6,6 +6,7 @@ "search.exclude": { "**/build": true, "**/coverge": true, - "**/node_modules": true + "**/node_modules": true, + "src/.linaria_cache": true } } diff --git a/generators/app/templates/config/babelTransform.js b/generators/app/templates/config/babelTransform.js new file mode 100644 index 0000000..9e532a7 --- /dev/null +++ b/generators/app/templates/config/babelTransform.js @@ -0,0 +1,7 @@ +const babelJest = require('babel-jest') + +module.exports = babelJest.createTransformer({ + presets: [require.resolve('babel-preset-react-app'), 'linaria/babel'], + babelrc: false, + configFile: false, +}) diff --git a/generators/app/templates/config/craco.config.js b/generators/app/templates/config/craco.config.js new file mode 100644 index 0000000..2c2194f --- /dev/null +++ b/generators/app/templates/config/craco.config.js @@ -0,0 +1,21 @@ +const { loaderByName, getLoader } = require('@craco/craco') +const transformBabelLoader = require('./transformBabelLoader') + +module.exports = { + webpack: { + configure: webpackConfig => { + const lm = getLoader(webpackConfig, loaderByName('babel-loader')) + const loader = lm.match.loader + webpackConfig.module.rules[2].oneOf[1] = transformBabelLoader(loader) + return webpackConfig + }, + }, + jest: { + configure: jestConfig => { + jestConfig.transform['^.+\\.(js|jsx|ts|tsx)$'] = require.resolve( + './babelTransform.js', + ) + return jestConfig + }, + }, +} diff --git a/generators/app/templates/config/transformBabelLoader.js b/generators/app/templates/config/transformBabelLoader.js new file mode 100644 index 0000000..7e38d92 --- /dev/null +++ b/generators/app/templates/config/transformBabelLoader.js @@ -0,0 +1,22 @@ +module.exports = loader => ({ + test: loader.test, + include: loader.include, + rules: [ + { + loader: loader.loader, + options: { + presets: [loader.options.presets[0], 'linaria/babel'], + }, + }, + { + loader: 'linaria/loader', + options: { + cacheDirectory: 'src/.linaria_cache', + sourceMap: process.env.NODE_ENV !== 'production', + babelOptions: { + presets: loader.options.presets, + }, + }, + }, + ], +}) diff --git a/generators/app/templates/package.json b/generators/app/templates/package.json index 6469fc4..fed4a8a 100644 --- a/generators/app/templates/package.json +++ b/generators/app/templates/package.json @@ -13,10 +13,10 @@ "homepage": "https://github.com/<%= githubUsername %>/<%= appname %>#readme", "scripts": { "clean": "rimraf coverage build *.log*", - "start": "react-scripts start", + "start": "craco start", "prebuild": "rimraf build", - "build": "react-scripts build", - "test": "react-scripts test", + "build": "craco build", + "test": "craco test", "eject": "react-scripts eject", "check-types": "tsc --noEmit", "precover": "rimraf coverage", @@ -65,6 +65,7 @@ "lcov" ] }, + "cracoConfig": "config/craco.config.js", "eslintConfig": { "extends": "react-app", "globals": { diff --git a/generators/app/templates/src/components/Home/Home.module.css b/generators/app/templates/src/components/Home/Home.module.css deleted file mode 100644 index 4d43059..0000000 --- a/generators/app/templates/src/components/Home/Home.module.css +++ /dev/null @@ -1,26 +0,0 @@ -.root { - text-align: center; -} - -.logo { - animation: spin infinite 20s linear; - height: 40vmin; - pointer-events: none; -} - -.header { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); -} - -@keyframes spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/generators/app/templates/src/components/Home/Home.tsx b/generators/app/templates/src/components/Home/Home.tsx index 2ac0c73..5db4051 100644 --- a/generators/app/templates/src/components/Home/Home.tsx +++ b/generators/app/templates/src/components/Home/Home.tsx @@ -1,3 +1,4 @@ +import { styled } from 'linaria/react' import React, { useEffect } from 'react' import Helmet from 'react-helmet' import { FormattedMessage, FormattedHTMLMessage } from 'react-intl' @@ -8,7 +9,6 @@ import useTheme from 'helpers/useTheme' import Theme from 'models/Theme' import RootState from 'store/RootState' -import styles from './Home.module.css' import logo from './logo.svg' interface StateProps extends Pick { @@ -31,12 +31,12 @@ const Home: React.FC = ({ }, [getCart]) useTheme(theme) return ( -
+ Home -
- logo +
+

@@ -48,8 +48,8 @@ const Home: React.FC = ({ {JSON.stringify(cart.subtotal.amount)} -
-
+ + ) } @@ -62,3 +62,29 @@ export default connect( getCart: fetchCart, }, )(Home) + +const Root = styled.div` + text-align: center; +` + +const Header = styled.div` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); +` + +const Logo = styled.img` + animation: spin infinite 20s linear; + height: 40vmin; + pointer-events: none; + @keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } + } +` diff --git a/generators/app/templates/src/helpers/resolveFetch.ts b/generators/app/templates/src/helpers/resolveFetch.ts index da530b3..954ab7e 100644 --- a/generators/app/templates/src/helpers/resolveFetch.ts +++ b/generators/app/templates/src/helpers/resolveFetch.ts @@ -1,6 +1,4 @@ /*eslint-disable no-throw-literal*/ -import { Omit } from 'ts-essentials' - import ResponseError from 'models/ResponseError' import ResponsePayload from 'models/ResponsePayload' @@ -61,5 +59,5 @@ export default async function resolveFetch( * Intentionally omit non-TBody props from the result type, * because errors have already been dealt with. */ - return (responsePayload as Omit) as TBody + return (responsePayload as unknown) as TBody } diff --git a/generators/app/templates/src/react-app-env.d.ts b/generators/app/templates/src/react-app-env.d.ts new file mode 100644 index 0000000..6431bc5 --- /dev/null +++ b/generators/app/templates/src/react-app-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/generators/app/templates/tsconfig.json b/generators/app/templates/tsconfig.json index 14c2eb9..3d84819 100644 --- a/generators/app/templates/tsconfig.json +++ b/generators/app/templates/tsconfig.json @@ -6,7 +6,6 @@ "allowUnusedLabels": false, "alwaysStrict": true, "baseUrl": "src", - "declaration": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "isolatedModules": true, diff --git a/generators/component/expected/barBaz/QuxQuux/QuxQuux.module.css b/generators/component/expected/barBaz/QuxQuux/QuxQuux.module.css deleted file mode 100644 index 4e0f8cb..0000000 --- a/generators/component/expected/barBaz/QuxQuux/QuxQuux.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.root { - display: block; -} diff --git a/generators/component/expected/barBaz/QuxQuux/QuxQuux.tsx b/generators/component/expected/barBaz/QuxQuux/QuxQuux.tsx index 31e8660..4352626 100644 --- a/generators/component/expected/barBaz/QuxQuux/QuxQuux.tsx +++ b/generators/component/expected/barBaz/QuxQuux/QuxQuux.tsx @@ -1,10 +1,9 @@ +import { styled } from 'linaria/react' import React from 'react' import { connect } from 'react-redux' import RootState from 'store/RootState' -import styles from './QuxQuux.module.css' - interface StateProps {} interface DispatchProps {} @@ -12,7 +11,11 @@ interface DispatchProps {} export interface QuxQuuxProps {} const QuxQuux: React.FC = () => ( -
content
+ content ) export default connect(null)(QuxQuux) + +const Root = styled.div` + display: block; +` diff --git a/generators/component/index.test.ts b/generators/component/index.test.ts index 414d8c4..f8c564f 100644 --- a/generators/component/index.test.ts +++ b/generators/component/index.test.ts @@ -22,7 +22,6 @@ describe('tsx:component', () => { 'barBaz/QuxQuux/index.ts', 'barBaz/QuxQuux/QuxQuux.tsx', 'barBaz/QuxQuux/QuxQuux.test.tsx', - 'barBaz/QuxQuux/QuxQuux.module.css', ] for (const filename of expectedFiles) { assert.fileContent( diff --git a/generators/component/index.ts b/generators/component/index.ts index d8f16eb..051929b 100644 --- a/generators/component/index.ts +++ b/generators/component/index.ts @@ -29,21 +29,19 @@ export = class extends Generator { const parts = this.options.name.split('/').map(camelCase) const name = sentenceCase(parts[parts.length - 1]) parts.splice(-1, 1, name) - ;['index.ts', 'Foo.tsx', 'Foo.test.tsx', 'Foo.module.css'].forEach( - filename => { - this.fs.copyTpl( - this.templatePath(filename), - this.destinationPath( - `src/components/${parts.join('/')}/${filename.replace( - /^Foo/, - name, - )}`, - ), - { + ;['index.ts', 'Foo.tsx', 'Foo.test.tsx'].forEach(filename => { + this.fs.copyTpl( + this.templatePath(filename), + this.destinationPath( + `src/components/${parts.join('/')}/${filename.replace( + /^Foo/, name, - }, - ) - }, - ) + )}`, + ), + { + name, + }, + ) + }) } } diff --git a/generators/component/templates/Foo.module.css b/generators/component/templates/Foo.module.css deleted file mode 100644 index 4e0f8cb..0000000 --- a/generators/component/templates/Foo.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.root { - display: block; -} diff --git a/generators/component/templates/Foo.tsx b/generators/component/templates/Foo.tsx index 42ca9d5..fb8e64d 100644 --- a/generators/component/templates/Foo.tsx +++ b/generators/component/templates/Foo.tsx @@ -1,10 +1,9 @@ +import { styled } from 'linaria/react' import React from 'react' import { connect } from 'react-redux' import RootState from 'store/RootState' -import styles from './<%= name %>.module.css' - interface StateProps {} interface DispatchProps {} @@ -12,7 +11,11 @@ interface DispatchProps {} export interface <%= name %>Props {} const <%= name %>: React.FC<<%= name %>Props & StateProps & DispatchProps> = () => ( -
content
+ content ) export default connect(null)(<%= name %>) + +const Root = styled.div` + display: block; +` diff --git a/package-lock.json b/package-lock.json index 3def13a..9e8370e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "generator-tsx", - "version": "0.1.0", + "version": "0.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 5379914..491dff6 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,10 @@ }, "main": "generators/app/index.js", "files": [ - "generators/**/*.js", - "generators/**/*.d.ts", + "generators/*.js", + "generators/*.d.ts", + "generators/*/*.js", + "generators/*/*.d.ts", "generators/*/templates/" ], "keywords": [ @@ -141,7 +143,7 @@ ] }, "scripts": { - "clean": "rimraf coverage *.log* generators/**/*.js generators/**/*.d.ts", + "clean": "rimraf coverage *.log* generators/*.js generators/*.d.ts generators/*/*.js generators/*/*.d.ts", "prebuild": "npm run clean", "build": "tsc && npm run prettier", "prettier": "prettier \"generators/**/*.{js,d.ts}\" --write",