Canvas Resume Editor - Monorepo+Rspack Engineering Practice
Prior to that we had a conversation aroundCanvas
Having talked a lot about the design level of the code, let's talk about engineering practices here. As I mentioned in the previous article, since it was done in the spirit of learning as well as curiosity about the technology, it was not possible to do much more than some tool libraries such asArcoDesign
、ResizeObserve
、Jest
In addition to packages such as Data Structurespackages/delta
Pluginizationpackages/plugin
Core Enginepackages/core
etc. are implemented manually, so in addition to learning about theCanvas
In addition to that, some project engineering practices were actually done.
- Online Editor./CanvasEditor
- Open source address./WindrunnerMax/CanvasEditor
with respect toCanvas
Related posts to resume editor project.
- The community keeps pushing Canvas on me, and I've learned Canvas to make a resume editor
- Canvas Graphics Editor - Data Structures and History (undo/redo)
- Canvas Graphics Editor - What data is in my clipboard?
- Canvas Resume Editor - Graphics Drawing and State Management (Lightweight DOM)
- Canvas Resume Editor - Monorepo+Rspack Engineering Practice
- Canvas Resume Editor - Hierarchical Rendering and Event Management Capability Designs
- Canvas Resume Editor - Check Draw & Drag & Drop Multi-Select Interaction Solution
Pnpm+Monorepo
Let's talk about why we need to usemonorepo
As an example, let's start with a pitfall I've stepped in before, in my previous rich text editor projectDocEditor It's just completely written in a separate singlesrc
directory, which was fine in the runtime of the project itself, but at the time I wanted to make the editor standalone as aNPM
package, which is packaged with the help of theRollup
There was nothing wrong with that either, the problem was with the referencing side. At the time, I introduced the document editor in the resume editor'sNPM
package, it was discovered that a module had been incorrectlyTreeShaking
up, and all still see that part of compatibility in the editor.
module: {
rules: [
{
// treat (sb a certain way)`doc-editor-light`(used form a nominal expression)`TreeShaking`It's a bit of a problem.
test: /doc-editor-light\/dist\/tslib.*\.js/,
sideEffects: true,
},
]
}
This problem led me to thedev
There is no problem in the mode, but in thebuild
This part of the code was then removed by mistake, causing the editor'swrapper
There was a problem with the nodes and elements such as lists were not being added correctly. Of course in reality this doesn't make the standalone package project bad, it only means that the whole administration may not be that simple, especially when packaged as aNPM
The package needs to be aware of the various entry issues. So now the rich text editor package that references me has become the4
Separate packages referenced separately, each in its own way, and the problem has not arisen again.
Speaking of packing, I've also stepped into a hole that I don't know if you've seen beforeReact
(used form a nominal expression)Invalid hook call
This classic reports an error. I found that this error was reported after I had previously unpacked it independently, but I was in theis labeled
peerDependencies "react": ">=16"
It is logical that the installation of the package will be directly applied hereReact
, it is unlikely that there will be a version inconsistency problem as far as theRules of Hooks
Definitely not possible either, as I was fine before, and it was only after unpacking that I had a problem. It finally turned out to be me inrollup
fail to mentionpeerDependencies
This part is parsed, resulting injsx-runtime
It was knocked into the bag, thoughReact
The versions are all17.0.2
But in fact two separate lexical scopes are runReact Hooks
, which has led to this problem.
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app See for tips about how to debug and fix this problem.
Moving on back to the project itself, the current project has abstracted away from the independentRspackMonoTemplate, and normally development creates repositories based on this template. The structure of the current resume editor projecttree -L 2 -I node_modules --dirsfirst
As follows.
CanvasEditor
│── packages
│ ├── core
│ ├── delta
│ ├── plugin
│ ├── react
│ └── utils
├──
├──
├──
└──
-
packages/core
: The core editor engine module for clipboard manipulation, event management, and state management,History
Module,Canvas
operations, selection operations, etc. are encapsulated here, equivalent to the implementation of the basicCanvas
Engine Capabilities. -
packages/delta
:: Data structure module, which designs benchmark data structures and implementsDeltaSet
data structures as well as the atomizedOp
operation, which is mainly used to describe the data structure of the whole editor as well as the operation, implements theinvert
and other capabilities that are essential to the realization ofHistory
Modules make a lot of sense. -
packages/plugin
: Plug-in module inpackages/delta
The plug-in capability was designed on top of theText
、Image
、Rect
and other plugins are implemented here. -
packages/react
:React
module, mainly for the view layer of the editor through the implementation of the editor, where it is more important that our core module is view-framework agnostic, if necessary the same can be done using theVue
、Angular
and other frameworks to implement the view layer. -
packages/utils
: Tool modules, mainly the packaging of some tool functions, such asFixedNumber
、Palette
and so on, these tool functions are used throughout the editor and are used as base packages throughout theworkspace
Cited in. -
: The whole
workspace
(used form a nominal expression), where some project information is configured.
EsLint
、StyleLint
The relevant configurations are also implemented here. -
:
pnpm
The lock file for the entireworkspace
The dependency version of the -
:
pnpm
(used form a nominal expression)workspace
Configuration file to configure themonorepo
The ability of the -
: The whole
workspace
(used form a nominal expression)tsconfig
configuration file to configure the entireworkspace
(used form a nominal expression)TypeScript
The compilation configuration, in this case, is used as the baseline configuration to provide references to modules in the project.
pnpm
itself is a very good package manager, saves disk space through hard and symbolic links, each version of the package only needs to be stored once, and most importantlypnpm
Created a non-flattenednode_modules
structure, thus ensuring strict matching of dependencies and declarations, strict control over dependency elevation, and the ability to avoid unexpected problems with dependency escalation, which improves the consistency and predictability of the project.
And back tomonorepo
,pnpm
Not only is it a very good package manager, its also provides an out-of-the-boxmonorepo
Ability. In thepnpm
There is afile, which is used to configure the
workspack
ratherpnpm
(used form a nominal expression)workspace
can then be used asmonorepo
capabilities, and our configurations are simple enough that we believe in thepackages
All directories in the directory are treated as subprojects.
packages:
- 'packages/*'
pass (a bill or inspection etc)monorepo
We can easily manage all sub-projects, especially for those who need to issueNpm
package projects, splitting the submodules is a good option, especially if it can be done in a way that the view layer is framework agnostic. In addition, themonorepo
There are also many benefits for the management of the whole project, for example, when packaging the whole application, instead of sending new packages for each sub-project before we can package it, we can just put the compilation process in theworkspace
level, which ensures consistency across the project, simplifies the build process and continuous integration flow, and allows all projects to share build scripts and tool configurations. In addition, all projects and modules share the same version control system, which facilitates unified version management and change tracking, and also helps to synchronize the update of dependencies between these projects.
TS+Rspack Best Practices
After all this talk of usingpnpm + monorepo
benefits that come with managing a program, let's talk more about what I've learned about theTS
together withRspack
apply toMonorepo
of best practices, I wonder if you've encountered two of these problems: the
- subprojects
TS
declaration changes do not take effect in real time, you must compile the subproject once to do so, and the subproject compilation process if you change thedist
and other product packages are removed, then thevsc
or other editors will reportTS
The error of not finding a reference to the declaration, this time it is necessary to use the command reReload TypeScript Project
to remove the error reporting. And if you don't remove the product package, there are some hidden problems, such as the original naming of a file asAt this point, for some reason it needs to be moved to the same name of the
a
directory and rename it toThen after executing this meal, it was found that if you change this point in the
The code won't update, you have to restart the app's
webpack
and other compilers to work, because it still references the original file, generating a similar problem is not complicated but it still takes time to troubleshoot. - Change the subitem's
TS
The code has to be recompiled for the subprojects because the project ismonorepo
managed inThere will be
workspace
references, and theworkspace
It's actually in thenode_modules
is referenced, so even though it's a sub-project it still needs to follow thenode_modules
for the rules to work, then it usually needs to be compiled into thejs
can be executed, so every time you change the code, you have to execute it all over again, which is very troublesome.-w
command to observe the change, but after all, it's an extra step, and if it's the presence of thealias
projects may simply use thetsc
to compile is not enough. In addition, themonorepo
We usually have a lot of sub-projects in the project, and if each sub-project needs to be like that, especially in this case where the compilation is done in full instead of incrementally, then the whole project will take a very long time to compile.
So let's look at the first problem here, the sub-projects ofTS
Statement changes don't take effect in real time, because as we also mentionedmonorepo
The sub-projects are actually passed through thenode_modules
to manage and reference, so its still required by default to follow thenode_modules
The rule that the(used form a nominal expression)
types
field pointing to theTS
declaration file, so is there any way we can modify this behavior? Of course there is, we have the entire project's rootconfigure
path
It will solve this problem perfectly. Once we have configured the following, we can set up a new file by pressing and holding theCtrl
When you add a left mouse click, you can jump to the root declaration of the sub-project. Also a point of concern here is that it's not recommended to configure the"baseUrl": "."
, there will be some odd path reference issues here and there, so in addition to packaging the resume editor project with theNpm
(used form a nominal expression)Other than that, they are configured directly using relative paths.
{
"compilerOptions": {
"...": "...",
"paths": {
"sketching-core": ["./packages/core/src"],
"sketching-delta": ["./packages/delta/src"],
"sketching-plugin": ["./packages/plugin/src"],
"sketching-utils": ["./packages/utils/src"],
},
},
"include": [
"packages/*/src"
]
}
Then solving the project'sTS
After declaring the problem, let's look at the compilation problem, which looks a bit more complicated here because theTS
declarations are simply type declarations that do not affect the compilation of the project code itself, except for compilation type checking. Then, in theRspack
should be configured in order for our code to point directly to the subproject, rather than having to go through thenode_modules
This set of rules, and it's actually quite simple here, just configure thewill work, so that when we directly modify the
TS
code, it also enables the editor to respond immediately to incremental compilation.
{
// ....
resolve: {
alias: {
"@": (__dirname, "./src"),
"sketching-core": (__dirname, "../core/src"),
"sketching-delta": (__dirname, "../delta/src"),
"sketching-plugin": (__dirname, "../plugin/src"),
"sketching-utils": (__dirname, "../utils/src"),
},
},
// ....
}
In fact, forRspack
In terms of what it has helped us do, like even thenode_modules
(used form a nominal expression)TS
files are also compiled, and for some files that have been passed through theCRA
createdwebpack
This is a little trickier to configure for projects, but of course we can also use thecustomize-cra
to do this, plus we'll have to turn off something like theModuleScopePlugin
plugin is the only way to do this, here is the rich text editor projectDocEditor The configuration of the
const src = (__dirname, "src");
const index = (__dirname, "src/");
const core = (__dirname, "../core/src");
const delta = (__dirname, "../delta/src");
const plugin = (__dirname, "../plugin/src");
const utils = (__dirname, "../utils/src");
= {
paths: function (paths) {
= src;
= index;
return paths;
},
webpack: override(
...[
// ...
addWebpackResolve({
alias: {
"doc-editor-core": core,
"doc-editor-delta": delta,
"doc-editor-plugin": plugin,
"doc-editor-utils": utils,
},
}),
babelInclude([src, core, delta, plugin, utils]),
// ...
configWebpackPlugins(),
].filter(Boolean)
),
};
In addition, the resume editor is a purely front-end project, and such a project has the great advantage of being able to run directly from static resources, whereas if we rely on theGitHub Action
It is then possible to pass theGit Pages
deployed directly in the repository and can be deployed directly through theGitHub Pages
access, so that the repository is presented with a completeDEMO
。
// .github/workflows/
name: deploy gh-pages
on:
push:
branches:
- master
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
persist-credentials: false
- name: install node-v16
uses: actions/setup-node@v3
with:
node-version: '16.16.0'
- name: install dependencies
run: |
node -v
npm install -g pnpm
pnpm config set registry /
pnpm install --registry=/
- name: build project
run: |
npm run build:react
- name: deploy project
uses: JamesIves/github-pages-deploy-action@releases/v3
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: gh-pages
FOLDER: packages/react/build
ultimate
Here we chat about why it's important to useMonorepo
As well as a brief chatpnpm workspace
strengths, and then addresses the sub-project development that will be encountered in theTS
compilation, and project compilation of two practical problems in theMonorepo
、Rspack
、Webpack
The relevant parts of the project were practiced, and finally there was a brief chat about utilizing theGitHub Action
directly in theGit Pages
Deployment OnlineDEMO
The first part of the article is about how to implement a hierarchical rendering and event management capability. Later in the article, we'll talk about how to implement the ability to design for hierarchical rendering and event management.