Location>code7788 >text

Canvas Resume Editor - Monorepo+Rspack Engineering Practice

Popularity:991 ℃/2024-09-19 11:19:51

Canvas Resume Editor - Monorepo+Rspack Engineering Practice

Prior to that we had a conversation aroundCanvasHaving 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 asArcoDesignResizeObserveJest In addition to packages such as Data Structurespackages/deltaPluginizationpackages/pluginCore Enginepackages/core etc. are implemented manually, so in addition to learning about theCanvasIn addition to that, some project engineering practices were actually done.

  • Online Editor./CanvasEditor
  • Open source address./WindrunnerMax/CanvasEditor

with respect toCanvasRelated 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 usemonorepoAs 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 singlesrcdirectory, which was fine in the runtime of the project itself, but at the time I wanted to make the editor standalone as aNPMpackage, which is packaged with the help of theRollupThere 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'sNPMpackage, it was discovered that a module had been incorrectlyTreeShakingup, 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 thedevThere is no problem in the mode, but in thebuildThis part of the code was then removed by mistake, causing the editor'swrapperThere 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 aNPMThe package needs to be aware of the various entry issues. So now the rich text editor package that references me has become the4Separate 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 callThis classic reports an error. I found that this error was reported after I had previously unpacked it independently, but I was in theis labeledpeerDependencies "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 HooksDefinitely 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 inrollupfail to mentionpeerDependenciesThis part is parsed, resulting injsx-runtimeIt was knocked into the bag, thoughReactThe versions are all17.0.2But 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 --dirsfirstAs follows.

CanvasEditor
│── packages
│   ├── core
│   ├── delta
│   ├── plugin
│   ├── react
│   └── utils
├── 
├── 
├── 
└── 
  • packages/core: The core editor engine module for clipboard manipulation, event management, and state management,HistoryModule,Canvasoperations, selection operations, etc. are encapsulated here, equivalent to the implementation of the basicCanvasEngine Capabilities.
  • packages/delta:: Data structure module, which designs benchmark data structures and implementsDeltaSetdata structures as well as the atomizedOpoperation, which is mainly used to describe the data structure of the whole editor as well as the operation, implements theinvertand other capabilities that are essential to the realization ofHistoryModules make a lot of sense.
  • packages/plugin: Plug-in module inpackages/deltaThe plug-in capability was designed on top of theTextImageRectand other plugins are implemented here.
  • packages/react: Reactmodule, 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 theVueAngularand other frameworks to implement the view layer.
  • packages/utils: Tool modules, mainly the packaging of some tool functions, such asFixedNumberPaletteand so on, these tool functions are used throughout the editor and are used as base packages throughout theworkspaceCited in.
  • : The wholeworkspace(used form a nominal expression), where some project information is configured.EsLintStyleLintThe relevant configurations are also implemented here.
  • : pnpmThe lock file for the entireworkspaceThe dependency version of the
  • : pnpm(used form a nominal expression)workspaceConfiguration file to configure themonorepoThe ability of the
  • : The wholeworkspace(used form a nominal expression)tsconfigconfiguration file to configure the entireworkspace(used form a nominal expression)TypeScriptThe compilation configuration, in this case, is used as the baseline configuration to provide references to modules in the project.

pnpmitself 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 importantlypnpmCreated a non-flattenednode_modulesstructure, 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 tomonorepopnpmNot only is it a very good package manager, its also provides an out-of-the-boxmonorepoAbility. In thepnpmThere is afile, which is used to configure theworkspackratherpnpm(used form a nominal expression)workspacecan then be used asmonorepocapabilities, and our configurations are simple enough that we believe in thepackagesAll directories in the directory are treated as subprojects.

packages:
  - 'packages/*'

pass (a bill or inspection etc)monorepoWe can easily manage all sub-projects, especially for those who need to issueNpmpackage 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, themonorepoThere 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 theworkspacelevel, 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 + monorepobenefits that come with managing a program, let's talk more about what I've learned about theTStogether withRspackapply toMonorepoof best practices, I wonder if you've encountered two of these problems: the

  • subprojectsTSdeclaration 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 thedistand other product packages are removed, then thevscor other editors will reportTSThe error of not finding a reference to the declaration, this time it is necessary to use the command reReload TypeScript Projectto 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 theadirectory and rename it toThen after executing this meal, it was found that if you change this point in theThe code won't update, you have to restart the app'swebpackand 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'sTSThe code has to be recompiled for the subprojects because the project ismonorepomanaged inThere will beworkspacereferences, and theworkspaceIt's actually in thenode_modulesis referenced, so even though it's a sub-project it still needs to follow thenode_modulesfor the rules to work, then it usually needs to be compiled into thejscan be executed, so every time you change the code, you have to execute it all over again, which is very troublesome.-wcommand to observe the change, but after all, it's an extra step, and if it's the presence of thealiasprojects may simply use thetscto compile is not enough. In addition, themonorepoWe 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 ofTSStatement changes don't take effect in real time, because as we also mentionedmonorepoThe sub-projects are actually passed through thenode_modulesto manage and reference, so its still required by default to follow thenode_modulesThe rule that the(used form a nominal expression)typesfield pointing to theTSdeclaration file, so is there any way we can modify this behavior? Of course there is, we have the entire project's rootconfigurepathIt will solve this problem perfectly. Once we have configured the following, we can set up a new file by pressing and holding theCtrlWhen 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'sTSAfter declaring the problem, let's look at the compilation problem, which looks a bit more complicated here because theTSdeclarations are simply type declarations that do not affect the compilation of the project code itself, except for compilation type checking. Then, in theRspackshould be configured in order for our code to point directly to the subproject, rather than having to go through thenode_modulesThis set of rules, and it's actually quite simple here, just configure thewill work, so that when we directly modify theTScode, 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, forRspackIn terms of what it has helped us do, like even thenode_modules(used form a nominal expression)TSfiles are also compiled, and for some files that have been passed through theCRAcreatedwebpackThis is a little trickier to configure for projects, but of course we can also use thecustomize-crato do this, plus we'll have to turn off something like theModuleScopePluginplugin 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 ActionIt is then possible to pass theGit Pagesdeployed directly in the repository and can be deployed directly through theGitHub Pagesaccess, 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 useMonorepoAs well as a brief chatpnpm workspacestrengths, and then addresses the sub-project development that will be encountered in theTScompilation, and project compilation of two practical problems in theMonorepoRspackWebpackThe relevant parts of the project were practiced, and finally there was a brief chat about utilizing theGitHub Actiondirectly in theGit PagesDeployment OnlineDEMOThe 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.