I created a drag and drop email editor, I’ve been working on it for the past month. It’s a lot of fun to work on, I completed it for first release, I’ll shed some light on the challenges I faced and how I solved them.
Why’s & What’s
why build this?
No free editors/open source editors that does the job,
but why not profit?
Not interested, I believe in Open Source. Heavily inspired by how LINUX (OPEN SOURCE) is useful for everyone now.
the real motivation?
Let me be frank here, I dont like unalyer github, MAINLY BECAUSE OF THEIR FALSE ADVERTISEMENT AS OPEN SOURCE, I understand that’s how the get their business going but, damn its scummy.
One by one respoectively
- scumbag unlayer,
- grapjs mjml,
- their implementation was cool with mjml
- company that I am working for, at the time of writing this, needed an email dnd editor.
Challenges
maintaining layer between MJML and Me.
writing parsers, serializers and deseriealizers.
design and code ui
manage drag and drop, mouse, key events, …etc.
deciding what kind of api to expose
deciding what inline editor to use
- I can do one of two things
- One, spend time & learn the api’s of an existing inline editor
- Two, spend time creating a yet another inline editor, that I have full control of. (I’ll learn a lot of new things If I do this)
I did both, but after finding serveral small size inline editors, I decided to completely go ahead with creating a custom InlineEditor, it is not a major task if I used document.executeCommand
maintaining demo/docs/npm package in single repo
backend
- using firebase fn in combination with express
- firestore for database
I’ll explain maintaining demo/docs/npm package in single repo now, since that is the recent & last thing that I completed.
Maintaining demo/docs/npm package in single repo
Let me fist explain the layout of the items in the repo.
the repo contains,
- create-react-app react-scripts by default for the demo page
- the editor component
- I want this to be bundled and published to npm
- the demo branch, that automatically deploys with github pages
- api docs for the project
checklist that I created before starting work on this
Its always better to plan ahead to avoid surprises later, created a small checklist to help me with this.
I should have,
- verified that the Editor is standalone
- (i.e). that there is no demo build dependencies directly in the editor (like on first editor load, fetch this template from the backend, ..etc)
- verified that the Editor is fully functional
- (i.e). that the editor can be used in a demo page without problems
- decided on what build system to use
- webpack
- rollup
- nd…etc, millions of other build systems out there
- created the build system, in such a way that It wont affect the demo build (created with create-react-app)
- at the time of writing this, react-scripts uses webpack v4, So installing another instance of webpack v5 for npm build, will cause react-scripts to fail, the same goes for its dependencies aswell.
- created a seperate branch, do all documentations and changes in that branch
- bundled only selected files in the npm build, should only contain
- editor
- css/sass files used with the editor
- image files used with the editor
- created sepereate scripts in package.json for the differnet builds
- build = for react-scripts-build
- build:module = for npm-build
- created a seperate packge.json for the npm build, since the exported Editor component doesn’t need all the dependencies from the repository.
Choosing build system for the editor
I have already published packages using webpack in the past, So I already knew webpack and its different modules. But the problem is I don’t know if I can use two webpack instances in a single project.
I Could go for a new build system just to solve this problem alone, Or find a way to use two webpack instances in a single project.
This is the point in time, where I found out about yarn alias, After I found this, I knew which build system to choose (webpack v5).
webpack has an environment configuration named ‘WEBPACK_PACKAGE’, it takes the package name of the webpack, So I can utilize this to install multiple versions, one with name ‘webpack’ another with alias ‘webpack-module’
"webpack-module": "npm:webpack@^5.60.0"
I can then run webpack normally with ‘react-scripts’ and ‘WEBPACK_PACKAGE=webpack-module webpack’ for npm build
Requirements for publishing to npm
For publishing to npm, I need,
- npm account
- a package.json with seperate dependencies from the demo package.json
- there should be a two package.json now. One for demo build, another for package build
- a webpack configuration file for bundling
- typescript definition file
- output of npm pack
Webpack configuration
webpack configuration should process
- process scss, css and module css, scss.
style loader - Inject the bundled styles into webpage.
since I used @import for fonts and url for background, I need to add css loader
I have also used sass, thus sass loader is required. To note, for the modern web, there is dart sass. I can instruct “sass loader” to use “dart sass”
// css
{
test: /(\.css$|\.module\.css$)/i,
use: ['style-loader-module', 'css-loader-module'],
},
// scss
{
test: /(\.s[ac]ss$|\.module\.s[ac]ss$)/i,
use: [
{
loader: 'style-loader-module',
},
{
loader: 'css-loader-module',
options: {
importLoaders: 1,
modules: {
mode: 'local',
},
},
},
{
loader: 'sass-loader-module',
options: {
implementation: require('sass'),
},
},
],
}
process .tsx, .ts and .jsx…etc.
{ test: /\.(js|jsx|ts|tsx)$/, exclude: /node_modules/, use: { loader: 'babel-loader-module', options: { presets: [ '@babel/preset-env', ['@babel/preset-react', { runtime: 'automatic' }], '@babel/preset-typescript', ], }, } }
instruct webpack to recognize .tsx file extensions.
resolve: { extensions: ['.js', '.jsx', '.ts', '.tsx'], }
a way to make image assets work in extensions,In webpack 5, I can bundle images inline in js files.
{ test: /\.svg/, type: 'asset/inline', }
That is it for the webpack config, Isn’t this simple and concise.
Maintaining another package.json for npm
Obviously, only at the time of packing I need package.json untill then I can renaming to something else, I named it to ‘module-package.json’ then at time of ‘bundling and packing’, I rename it to ‘package.json’
the contents of ‘module-package.json’ should contain the following,
"name": "dnd-email-editor",
"version": "v0.0.0-alpha.3",
"description": "Drag and Drop Editor designed for mails",
"main": "./main.js",
"files": ["**/*"],
"types": "./index.d.ts",
"peerDependencies": {
"react": "^17.0.1",
"react-dom": "^17.0.1"
},
"author": "https://github.com/aghontpi",
"license": "GPL-3.0",
"bugs": {
"url": "https://github.com/aghontpi/dnde/issues"
},
"homepage": "https://github.com/aghontpi/dnde",
On every version change, change “version” key, It’ll automatically update in npmjs.
bundling and packing
I added the following command to package.json, (original package.json not ‘module-package.json’), note the environment variable ‘WEBPACK_PACKAGE’ I mentioned from before.
WEBPACK_PACKAGE=webpack-module webpack &&
cp module-package.json module/package.json &&
cp index.d.ts module/index.d.ts &&
cp README.md module/README.md &&
npm pack ./module/
I can the publish the output from the previous command with
npm publish build-verion.tgz
Conclusion
I oversimplified in many cases but this covers ‘publishing to npm part’. I solved a lot of similar interesting problems in the project, Check it github dnde.