Introduction
Usually, we should not publish TypeScript code to the NPM database, except for the typings. The normal approach is to convert the code into CommonJS (cjs) and/or ES modules (esm) and then publish. An easy way to do this is with RollupJS.
Nonetheless, it is difficult to convert the components that use Emotion to reusable modules. Moreover, why not making our TypeScript code directly available? (aiming at reusing it later in a React application).
Here, we explain our approach to publish a bunch of simple-but-powerful React 17 components, written in TypeScript and with Emotion, that we use in a number of projects (e.g. https://epop.net.br).
The code is available on GitHub: https://github.com/cpmech/rcomps
Contents
Publishing TypeScript components
NOTE: This procedure uses yarn
, rsync
, and jq
.
The idea is quite simple. First, copy all the .ts
and .tsx
files to the dist
directory when calling yarn build
. To do so, add this command to package.json
:
"build": "rm -rf dist && mkdir -p dist/rcomps && rsync -av src/components/* dist/rcomps --exclude __stories__ --exclude __tests__"
Above, we remove any existent dist
directory, create a subdirectory named dist/rcomps
, which will help the consumer of this library to move it to the right place, and then copy the files using rsync
(ignoring storybook and jest files).
Second, for the yarn dist
command, copy an alternative package-dist.json file to the dist
directory by using this package.json
command:
"dist": "yarn build && cp package-dist.json dist/package.json && cd dist/ && yarn publish --access=public || true && cd .."
Above, we call the build command, copy the two package-json files into dist, and then call yarn publish
.
The alternative (distributable) package-dist.json
is:
{
"name": "<YOUR NPM PROJECT HERE>",
"version": "0.1.0",
"license": "MIT",
"scripts": {
"fix:ver1": "jq '.version = $newVal' --arg newVal `jq -r '.version' package.json` ../package.json > tmp.$$.json && mv -f tmp.$$.json ../package.json",
"fix:ver2": "jq '.version = $newVal' --arg newVal `jq -r '.version' package.json` ../package-dist.json > tmp.$$.json && mv -f tmp.$$.json ../package-dist.json",
"git:add": "git add ../package.json ../package-dist.json",
"git:tag": "git tag v`jq -r '.version' package.json`",
"git:all": "yarn git:add && git commit -m 'Publish' && yarn git:tag && git push && git push --tags",
"postpublish": "yarn fix:ver1 && yarn fix:ver2 && yarn git:all"
},
"repository": "<YOUR GITHUB REPO HERE>",
"description": "TypeScript React Components with Emotion"
}
The above code will automatically tag the git repository and fix the version numbers in both the main package.json and the publishable (alternative) package-dist.json. Each sub-command is explained below:
-
fix:ver1
fixes the version in the parent package.json file -
fix:ver2
fixes the version in the distributable package-dist.json file -
git:add
takes the modifications in package.json and package-dist.json -
git:tag
creates a new tag based on the updated version in package.json -
git:all
runs "all" git commands, including adding, tagging, committing, pushing, and pushing tags -
postpublish
is the hook, called automatically byyarn publish
, that will call the above sub-commands.
Reusing TypeScript Components
To reuse the TypeScript components, we have to copy the .ts
and .tsx
files from node_modules
to the right place (e.g. the src
directory of your create-react-app
project). To do so, we add the following command to the scripts section of the app's package.json:
"postinstall": "bash ./scripts/npm_postinstall.bash"
The above command will be automatically called whenever we run yarn install
.
The auxiliary npm_postinstall.bash
script is:
#!/bin/bash
set -e
if [[ -d "./node_modules/@cpmech/rcomps/rcomps" ]]; then
echo ">>> moving rcomps to src <<<"
rm -rf ./src/rcomps
mv ./node_modules/@cpmech/rcomps/rcomps ./src/
fi
The above script simply removes any existent ./src/rcomps
directory from your React app and copy it again from the node_modules
. NOTE: we have to move the rcomps
directory from node_modules
because react-scripts start
or react-scripts test
may complain if they find TypeScript files inside node_modules
.
Now, you can simply (for example):
yarn add @cpmech/rcomps
EmotionJS and React 17
Emotion is fantastic! It allows you to write real CSS to style your component. So, everything is possible! For example, this is a piece of CSS for our HTML input element see our inputElementCss.ts file:
const common = `
position: relative;
height: ${height}px;
margin-top: ${marginTop}px;
width: ${width};
input {
font-size: ${fontSize}px;
box-sizing: border-box;
height: ${height}px;
width: 100%;
padding-left: ${paddingHoriz}px;
padding-right: ${pickerMode ? paddingRightPicker : paddingHoriz}px;
border: ${borderWidth}px solid ${borderColor};
border-radius: ${borderRadius}px;
${flatLeft ? `border-top-left-radius:0;border-bottom-left-radius:0;` : ''}
${flatRight ? `border-top-right-radius:0;border-bottom-right-radius:0;` : ''}
color: ${textMode ? mutedColor : color};
background-color: ${bgColor};
resize: none;
outline: none;
${pickerMode && !textMode ? `cursor:pointer;` : ''}
${
pickerMode
? `text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;`
: ''
}
}
input[required] + label[placeholder] {
display: block;
pointer-events: none;
line-height: ${labelFontSize}px;
margin-top: -${deltaLabel}px;
}
`;
Note above the use of input[required] + label[placeholder]
that we cannot do with standard React.
To use EmotionJS with React 17, where we don't need to import React anymore, simply add the following line to the top of your TypeScript code:
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';
And then use like this:
export const RcProgress: React.FC<RcProgressProps> = ({
progress,
color = '#ffffff',
backgroundColor = '#e5e5e5',
borderColor,
barColor = '#4d50c6',
borderRadius = 300,
}) => {
const p = progress || 0;
const width = p < 0 ? 0 : p > 100 ? 100 : p;
return (
<div
css={css`
${backgroundColor ? `background-color: ${backgroundColor};` : ''}
${borderColor ? `border: 1px solid ${borderColor};` : ''}
border-radius: ${borderRadius}px;
`}
>
<div
css={css`
height: 24px;
width: ${width}%;
color: ${color};
font-weight: bold;
font-size: 15px;
line-height: 1.5;
background-color: ${barColor};
border-radius: ${borderRadius}px;
text-align: center;
padding-top: 1px;
`}
>
{width > 3 && `${width}%`}
</div>
</div>
);
};
Conclusion
The development process with TypeScript and React 17 is quite enjoyable these days. The create-react-app makes the bootstrapping process much easier (e.g. yarn create react-app myapp --template typescript
). We also suggest using EmotionJS for styling the React components because we can do much more than with standard React. Finally, it is possible and convenient to make your TypeScript Components directly available on NPM.
Top comments (0)