jumpcut v0.1.0
All of Jumpcut's components that are able to be shared across projects are hosted here.
The problem
We have spent a lot of money on branding. We've made the investment, and now we need that branding to be implemented consistently across our projects. Things like buttons, panels, lists, and even larger components and utility scripts can all be shared across projects. But how do we do that efficiently? Copying code and trying to maintain updates of components across projects is an absolute nightmare.
The solution
A repo and node package that will be published to NPM and imported across our projects. This repo has CI that automatically deploys the entire package to NPM. Whenever other projects that depend on this package are deployed, CI will update the project's NPM packages, automatically keeping our dependencies up-to-date.
Best practices
When modifying or extending existing components, you must keep in mind that these packages are used across projects and automatically updated. That means your OPEN-CLOSED principle is more important than ever here. If you're not familiar with the OPEN-CLOSED principle, you should be. Maybe checkout this article specifically related to javascript: Open-Closed Principle.
Lerna and Monorepos
Our shared component repo follows a popular structure called a Monorepo. That means we have multiple NPM packages managed in a single repo. We use a dev dependency called Lerna
. Lerna is a tool for managing multiple packages contained in a single repo (or Monorepo).
The overall repo is an NPM package named jumpcut
. You may npm i jumpcut
and individually import each of the packages you use (import Button from 'jumpcut/components/Button';
), or you may npm i @jumpcut/components-Button
and only install a specific package. Each of the types (component
, script
, and web
) are also scoped packages with the names @jumpcut/components
, @jumpcut/scripts
, and @jumpcut/web
that can be installed individually as well.
Commands
npm run generate [type] [name]
will execute a node script that accepts two required arguments. This does a couple of things. First, it generates the folder structure,index.js
, andpackage.json
files for the module in thesrc
folder. Second, it generates and modifies the relevant import files and package.json for deployment to and import from NPM.type
- an enum['component', 'script', 'web']
, wherecomponent
is a React component,script
is a vanilla js script, andweb
is a Web Component.['c', 's', 'w']
abbreviations may also be used as thetype
argument.name
- the script or component file name without an extension. This will be the import name. NOTE: All shared components must be default exports in their root. You cannot have a component or script that has named exports. This is an unfortunate limitation required to keep imports easy and manageable.
npm run dependecy [targetType] [targetName]
will add a dependency to the target source package by prompting you for thetype
andname
of the package you want to add as a dependency. Often times we will want to use a shared package in another shared package. That is a traditional npm package dependency, but in a Monorepo environment we need to manage these dependencies a little differently because versions are changing across dependencies all the time. This is largely managed by Lerna, and this script takes some of the manual work and hides that Lerna CLI to reduce the developer's overhead.npm run deploy
simply hides the lerna command to reduce dev overhead. Thelerna publish -y
command will look at any packages that have been modified by the developer and increment the versions of those packages before publishing them to NPM. If you edit ascript
package and publish it, you'll have to increment the version of the package (eg.@jumpcut/scripts-uuid-validator
) and thetype
scope of the package (eg.@jumpcut/scripts
) and finally the root packagejumpcut
.npm run rename [type] [name]
andnpm run move [type] [name]
do the same thing. You will be prompted for a newtype
(just press Enter to keep the current type) and prompted for a newname
. This is useful if you want to rename a component or move a React component to a web component.npm run delete [type] [name]
will trigger a prompt to confirm deletion of a package with a giventype
andname
.
Usage - Your CRUD Operations
CREATE a new shared component/script
- Run
npm run generate [type] [name]
to generate the files. - Edit the files in
~/src/[type]/[name]/
to your liking (actually write the component/script). - When your done and tested on Storybook, commit your changes to the repo.
- Run
npm run deploy
.
READ (use) an existing shared component
Lerna automatically publishes this core jumpcut
package and all sub modules from this repo as NPM modules.
npm i jumpcut
will install the entire jumpcut shared repo as a dependency.
Packages can then be imported in a variety of traditional ways:
import Button from 'jumpcut/components/Button'
// OR
import components from 'jumpcut/components';
const Component = components.Button
...
return <Component />
// Due to the default export restriction, what you CANNOT do is:
import { Button } from 'jumpcut/components';
npm i @jumpcut/components
will install JUST the shared react components from our jumpcut shared repo. Then you just import from there:
import Button from '@jumpcut/components/Button';
npm i @jumpcut/components-button
will install a specific shared component. The naming convention is the @jumpcut
scope, and the package is named @jumpcut/[type]-[name]
in all lower case. In which case you would import:
import Button from '@jumpcut/components-button';
When it comes to web
components, you would use a monkey-patch import. For example:
import 'jumpcut/web/Button';
// OR
import '@jumpcut/web/Button';
// OR
import '@jumpcut/web-button';
UPDATE an existing shared component
Whenever you want to make a change to an existing shared component (keep in mind the OPEN/CLOSED Princple!), you can simply make the changes to the source files, commit your changes to the repo, and run npm run deploy
. If you need to rename a file, or move it from one type (eg. component
) to another (eg. web
), then run npm run rename [type] [name]
or npm run move [type] [name]
, respectively.
DELETE an existing shared coument
Run npm run delete [type] [name]
.
Cross-package dependencies
Often times we will want to use one shared package in another shared package. In such a case you will run the command npm run dependency [targetType] [targetName]
;
targetType
- The TYPE (enum['component', 'script', 'web']
) of the package that you want to add a dependency to.targetName
- The NAME of the package that you want to add a dependency to. You will be prompted for thetype
andname
of the package you want to add as a dependency. This process adds the input prompted package as a dependency in thepackage.json
of the argument package. It then runslerna bootstrap
which manages those dependency versioning going forward.
Naming Conventions
React components
will follow PascalCase for both file names and import names.
scripts
will follow kebab-case for file names, and camelCase for import names.
web
components will follow kebab-case for file names (matching their html tag name) and imported using monkey-patching.
// React components
import Button from '@jumpcut/components/Button';
// Scripts
import uuidValidator from '@jumpcut/scripts/uuid-validator';
// Web components
import '@jumpcut/web/jc-button';
React
When using web components in react
projects run npm i @webcomponents/webcomponentsjs vendor-copy
Then add the following entry to your project's root package.json
.
"scripts" {
...
"postinstall": "vendor-copy"
},
...
"vendorCopy": [
{
"from": "node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js",
"to": "public/vendor/custom-elements-es5-adapter.js"
},
{
"from": "node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js",
"to": "public/vendor/webcomponents-bundle.js"
}
]
and add the following to the head
of your view file.
<script src="%PUBLIC_URL%/vendor/webcomponents-bundle.js"></script>
<script>if (!window.customElements) { document.write("<!--"); }</script>
<script src="%PUBLIC_URL%/vendor/custom-elements-es5-adapter.js"></script>
<!--! DO NOT REMOVE THIS COMMENT, WE NEED ITS CLOSING MARKER -->
and then you can simply use a monkey-patch import
statement like so:
import '@jumpcut/web/jc-button';
...
render() {
return (
<jc-button id="example" />
);
}
...
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago