React components are packaged and released to npm (create-react-app+ts+cesium)

React components are packaged and released to npm (create-react-app + ts + cesium) and continuously updated~

  • Build react framework
    • Scaffolding generates react framework
    • Expose configuration file
    • Configure alias
    • Run the project
  • Reconstruction project
    • svg error
    • cesium related configuration
    • codemirror configuration
    • Reference content outside the src file in react
  • Pack
    • Package entry and exit configuration
    • Package and output the ts declaration file according to the project structure
    • Package and introduce another project for testing
  • npm release process
    • Login npm
    • release

The latest new requirement is to make a component and publish it on npm. This project uses React18 and uses the react framework generated by the create-react-app scaffolding. The project includes ts, cesium, etc.

The previous project was pure js and needed to be transformed first.

Build a react framework

Scaffolding generates react framework

Enter the command in the terminal:

npx create-react-app my-app --template typescript

Exposed configuration file

Because there are many webpack configurations that need to be changed in the project, the configuration files are directly exposed.
Enter the command in the terminal:

npm run eject
Note: Changing the content in the react framework may cause eject to fail. You can expose the file first and then perform other operations.

Configure alias

Find the webpack.config.js file in the config folder and search for alias (alias under the resolve object)
Modify the code as follows:

alias: {<!-- -->
        // Support React Native Web
        // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
        'react-native': 'react-native-web',
        // Allows for better profiling with ReactDevTools
        ...(isEnvProductionProfile & amp; & amp; {<!-- -->
          'react-dom$': 'react-dom/profiling',
          'scheduler/tracing': 'scheduler/tracing-profiling',
        }),
        ...(modules.webpackAliases || {<!-- -->}),
        // Custom alias
        '@': path.resolve('src'),
},

Because the project uses ts, you need to find the tsconfig.json file
Modify the code as follows:

{<!-- -->
  "compilerOptions": {<!-- -->
    "target": "es5",//The target language converted into
    "lib": [
      "dom",
      "dom.iterable",
      "esnext",
      "es2015"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": false,
    "jsx": "react-jsx",
    // alias
    "paths": {<!-- -->
      "@/*": [
        "./src/*"
      ],
    },
  },
  "include": [
    "src",
  ],
  "exclude": ["node_modules", "build",]
}

Run the project

Enter the command in the terminal:

yarn start

At this point the project runs successfully!

Renovation project

If this section is not needed, you can proceed to the next step. This section only records the problems encountered during the project transformation.

svg error

The console error message is as follows:

Uncaught Error: Module build failed (from ./node_modules/@svgr/webpack/dist/index.js):
SyntaxError: unknown file: Namespace tags are not supported by default. React's JSX doesn't support namespace tags. You can set `throwIfNamespace: false` to bypass this warning. (at universalModuleDefinition:15:1)
Module build failed (from ./node_modules/@svgr/webpack/dist/index.js):
SyntaxError: unknown file: Namespace tags are not supported by default. React's JSX doesn't support namespace tags. You can set `throwIfNamespace: false` to bypass this warning.
   5 | titleId,
   6 | ...props
> 7 | }, ref) => <svg width={<!-- -->20} height={<!-- -->20} xmlns="http://www.w3.org/2000 /svg" xmlns:svg="http://www.w3.org/2000/svg" ref={<!-- -->ref} aria-labelledby={<!-- -->titleId } {<!-- -->...props}>{<!-- -->title ? <title id={<!-- -->titleId}>{<!-- -->title} </title> : null}<path fill="#aaa" d="m3,9l2,0l0,-4l4,0l0,-2l-6,0l0,6l0,0z" /><path fill= "#aaa" d="m11,3l0,2l4,0l0,4l2,0l0,-6l-6,0l0,0z" /><path fill="#aaa" d="m15, 15l-4,0l0,2l6,0l0,-6l-2,0l0,4l0,0z" /><path fill="#aaa" d="m5,11l-2,0l0,6l6,0l0, -2l-4,0l0,-4l0,0z" /></svg>;
     | ^^^^^^^^^
   8 | const ForwardRef = forwardRef(SvgFullscreen);
   9 | export {<!-- --> ForwardRef as ReactComponent };
  10 | export default __webpack_public_path__ + "static/media/fullscreen.b7fda0177c8659d65ded39b4dd144ca8.svg";

As shown in the picture:

Solution:
Modify the webpack configuration and find the config folder in the root directory > find the webpack.config.js file.
Find the module object in the webpack.config.js file and find the rules array.
Modify the rules as shown below:

code show as below:

{<!-- -->
              test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, /\.svg$/], //Add, /\.svg$/Solve packaging and error reporting issues
              type: "asset",
              parser: {<!-- -->
                dataUrlCondition: {<!-- -->
                  maxSize: imageInlineSizeLimit,
                },
              },
            },

Modifying this configuration will also solve the problem that the svg path cannot be found when referenced in other projects after packaging.

cesium related configuration

The version of cesium used in this project is 1.110.0.
Need to download plug-in:

  • cesium
  • copy-webpack-plugin
  • node-polyfill-webpack-plugin

The versions are as follows:

"cesium": "^1.110.0",
"copy-webpack-plugin": "^11.0.0",
"node-polyfill-webpack-plugin": "^2.0.1",

The download command is as follows:

yarn add cesium -D
yarn add copy-webpack-plugin -D
yarn add node-polyfill-webpack-plugin -D

Find the config folder in the root directory > find the webpack.config.js file
Add the following code to the webpack.config.js file:

/* cesium */
const CopyWebpackPlugin = require("copy-webpack-plugin");
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
const cesiumSource = "node_modules/cesium/Source";
const cesiumWorkers = "../Build/Cesium/Workers";

//The following code needs to be added to the plugins array
new CopyWebpackPlugin({<!-- -->
patterns: [
{<!-- --> from: path.join(cesiumSource, cesiumWorkers), to: "Workers" },
        {<!-- --> from: path.join(cesiumSource, "Assets"), to: "Assets" },
        {<!-- --> from: path.join(cesiumSource, "Widgets"), to: "Widgets" },
],
}),
new NodePolyfillPlugin(),
new webpack.DefinePlugin({<!-- -->
// env.stringified,
CESIUM_BASE_URL: JSON.stringify(""),
}),

The configuration is as shown in the figure:


Instantiate cesium code:

let viewer = new Cesium.Viewer(mapContainer, {<!-- -->
      animation: false, //Whether to create an animated widget, instrument in the lower left corner
      baseLayerPicker: false, //Whether to display the layer selector
      fullscreenButton: false, //Whether to display the full screen button
      geocoder: false, //Whether to display the geocoder widget, query button in the upper right corner
      homeButton: false, //Whether to display the Home button
      infoBox: false, //Whether to display the information box
      sceneModePicker: false, //Whether to display the 3D/2D selector, and scene3DOnly cannot be true at the same time
      selectionIndicator: false, //Whether to display the selection indicator component
      timeline: false, //Whether to display the timeline
      navigationHelpButton: false, //Whether to display the help button in the upper right corner
      scene3DOnly: false, //If set to true, all geometries are drawn in 3D mode to save GPU resources
      // terrainProvider: Cesium.createWorldTerrain(),
      orderIndependentTranslucency: false,
      contextOptions: {<!-- -->
        //Allow canvas to convert image convertToImage in cesium state
        webgl: {<!-- -->
          alpha: true,
          depth: false,
          stencil: true,
          antialias: true,
          premultipliedAlpha: true,
          preserveDrawingBuffer: true,
          failIfMajorPerformanceCaveat: true,
        },
        allowTextureFilterAnisotropic: true,
      },
      // baseLayer: ,
});

viewer.imageryLayers.addImageryProvider(
      new Cesium.UrlTemplateImageryProvider({<!-- -->
        url: "address xxxxxxxxxx",
        maximumLevel: 18,
      })
);
viewer.scene.sun.show = false;
viewer.scene.moon.show = false;
viewer.scene.skyBox.show = true;
viewer.scene.backgroundColor = new Cesium.Color(0.0, 0.0, 0.0, 0.0);

codemirror configuration

The versions are as follows:

"@codemirror/lang-javascript": "^6.2.1",
"@types/codemirror": "^5.60.12",
"codemirror": "5.38.0",

Different versions have different instantiation methods, and a style theme needs to be introduced to take effect.
code show as below:

import CodeMirror from "codemirror";
import {<!-- --> javascript } from "@codemirror/lang-javascript";
import "codemirror/lib/codemirror.css";
import "codemirror/theme/monokai.css"; //theme style file
import "codemirror/mode/javascript/javascript"; //javascript mode
import "codemirror/addon/hint/show-hint.css";
import "codemirror/addon/hint/show-hint";

// var codemirror = new EditorView({<!-- -->
// extensions: [basicSetup, javascript()],
// parent: container.dom!,
// });
var codemirror = CodeMirror(container.dom, {<!-- -->
    value: "",
    lineNumbers: true,
    lineWrapping: true,
    matchBrackets: true,
    indentWithTabs: true,
    tabSize: 4,
    indentUnit: 4,
    mode: "javascript",
  });
codemirror.setOption("theme", "monokai");
codemirror.on("change", function () {<!-- -->
    if (codemirror.state.focused === false) return;
    clearTimeout(delay);
    delay = setTimeout(function () {<!-- -->
      if (errorLine) {<!-- -->
        codemirror.removeLineClass(errorLine, "CodeMirror-errorLine");
        errorLine = null;
      }

      if (currentScript !== null) {<!-- -->
        currentScript.source = codemirror.getValue();

        editor.signals.scriptChanged.dispatch();
      } else if (currentEffect !== null) {<!-- -->
        var error;
        var currentSource = currentEffect.source;

        editor.timeline.reset();

        try {<!-- -->
          currentEffect.source = codemirror.getValue();
          editor.compileEffect(currentEffect);
        } catch (e) {<!-- -->
          error = e.name + " : " + e.message; // e.stack, e.columnNumber, e.lineNumber

          if (/Chrome/i.test(navigator.userAgent)) {<!-- -->
            var result = /<anonymous>:([0-9] + ):([0-9 + ])/g.exec(e.stack);
            if (result !== null) errorLine = parseInt(result[1]) - 3;
          } else if (/Firefox/i.test(navigator.userAgent)) {<!-- -->
            var result = /Function:([0-9] + ):([0-9 + ])/g.exec(e.stack);
            if (result !== null) errorLine = parseInt(result[1]) - 1;
          }

          if (errorLine !== null) {<!-- -->
            codemirror.addLineClass(
              errorLine,
              "errorLine",
              "CodeMirror-errorLine"
            );
          }
        }

        editor.timeline.update(editor.player.currentTime);

        if (error !== undefined) {<!-- -->
          errorDiv.setDisplay("");
          errorText.setValue("? " + error);

          currentEffect.source = currentSource;
        } else {<!-- -->
          errorDiv.setDisplay("none");
        }
      }
    }, 1000);
});

React refers to content outside the src file

Because the framework generated by the create-react-app scaffolding prohibits references from outside the src directory, the webpack configuration needs to be modified.

The webpack.config.js file modification code is as follows:
Find the resolve object-plugins array

plugins: [
        // Prevents users from importing files from outside of src/ (or node_modules/).
        // This often causes confusion because we only process files within src/ with babel.
        // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
        // please link the files into your node_modules/ and let module-resolution kick in.
        // Make sure your source files are compiled, as they will not be processed in any way.
        new ModuleScopePlugin(paths.appRoot, [
          paths.appPackageJson,
          reactRefreshRuntimeEntry,
          reactRefreshWebpackPluginRuntimeEntry,
          babelRuntimeEntry,
          babelRuntimeEntryHelpers,
          babelRuntimeRegenerator,
        ]),
],

Find the paths.js file under config
Fill in the above imported path in the paths.js file
code show as below:

module.exports = {<!-- -->
  dotenv: resolveApp('.env'),
  appPath: resolveApp('.'),
  appBuild: resolveApp(buildPath),
  appPublic: resolveApp('public'),
  appHtml: resolveApp('public/index.html'),
  appIndexJs: resolveModule(resolveApp, 'src/index'),
  buildEntry: resolveModule(resolveApp, 'src/gis'),
  appPackageJson: resolveApp('package.json'),
  appSrc: resolveApp('src'),
  appTsConfig: resolveApp('tsconfig.json'),
  appJsConfig: resolveApp('jsconfig.json'),
  yarnLockFile: resolveApp('yarn.lock'),
  testsSetup: resolveModule(resolveApp, 'src/setupTests'),
  proxySetup: resolveApp('src/setupProxy.js'),
  appNodeModules: resolveApp('node_modules'),
  appWebpackCache: resolveApp('node_modules/.cache'),
  appTsBuildInfoFile: resolveApp('node_modules/.cache/tsconfig.tsbuildinfo'),
  swSrc: resolveModule(resolveApp, 'src/service-worker'),
  publicUrlOrPath,
  appRoot: resolveApp(''),
};

Packaging

Packaging entrance and exit configuration

Because the final export of the project is changed to js, the name and path of the packaged js file are modified.
webpack.config.js file, find the output object
Modify the code as follows:

output: {<!-- -->
      // The build folder.
      path: paths.appBuild,
      // Add /* filename */ comments to generated require()s in the output.
      pathinfo: isEnvDevelopment,
      // There will be one main bundle, and one file per asynchronous chunk.
      // In development, it does not produce real files.
      // filename: isEnvProduction
      // ? 'static/js/[name].[contenthash:8].js'
      // : isEnvDevelopment & amp; & amp; 'static/js/bundle.js',
      filename: "index.js",//Export js file path and name
      library: {<!-- -->
        name: "library", //Library output variable name
        type: "umd", //Library type
        export: "Gis", //Export variable name
        auxiliaryComment: "test component",
      },
      // There are also additional JS chunk files if you use code splitting.
      chunkFilename: isEnvProduction
        ? "static/js/[name].[contenthash:8].chunk.js"
        : isEnvDevelopment & amp; & amp; "static/js/[name].chunk.js",
      assetModuleFilename: "static/media/[name].[hash][ext]",
      // webpack uses `publicPath` to determine where the app is being served from.
      // It requires a trailing slash, or the file assets will get an incorrect path.
      // We inferred the "public path" (such as / or /my-project) from homepage.
      publicPath: paths.publicUrlOrPath,
      // Point sourcemap entries to original disk location (format as URL on Windows)
      devtoolModuleFilenameTemplate: isEnvProduction
        ? (info) =>
          path
            .relative(paths.appSrc, info.absoluteResourcePath)
            .replace(/\/g, "/")
        : isEnvDevelopment & amp; & amp;
        ((info) =>
          path.resolve(info.absoluteResourcePath).replace(/\/g, "/")),
    },

Package and output ts declaration files according to the project structure

The webpack generated in the create-react-app scaffold uses babel-loader instead of ts-loader to compile ts, so you need to add the ts-loader dependency.

The ts-loader version is as follows:

"ts-loader": "^9.5.0"

Modify webpack.config.js:

//Change the code to the code before modification
{<!-- -->
              test: /\.(js|mjs|jsx|ts|tsx)$/,
              include: paths.appSrc,
              loader: require.resolve('babel-loader'),
              options: {<!-- -->
                customize: require.resolve(
                  'babel-preset-react-app/webpack-overrides'
                ),
                presets: [
                  [
                    require.resolve('babel-preset-react-app'),
                    {<!-- -->
                      runtime: hasJsxRuntime ? 'automatic' : 'classic',
                    },
                  ],
                ],

                plugins: [
                  isEnvDevelopment & amp; & amp;
                  shouldUseReactRefresh & amp; & amp;
                  require.resolve('react-refresh/babel'),
                ].filter(Boolean),
                // This is a feature of `babel-loader` for webpack (not Babel itself).
                // It enables caching results in ./node_modules/.cache/babel-loader/
                // directory for faster rebuilds.
                cacheDirectory: true,
                // See #6846 for context on why cacheCompression is disabled
                cacheCompression: false,
                compact: isEnvProduction,
              },
            },

//The modified code is as follows
{<!-- -->
              test: /\.(ts|tsx)$/,
              loader: require.resolve("ts-loader"),
              options: {<!-- -->
                transpileOnly: false,
              },
            },
            {<!-- -->
              test: /\.(js|mjs|jsx)$/,
              include: paths.appSrc,
              loader: require.resolve("babel-loader"),
              options: {<!-- -->
                customize: require.resolve(
                  "babel-preset-react-app/webpack-overrides"
                ),
                presets: [
                  [
                    require.resolve("babel-preset-react-app"),
                    {<!-- -->
                      runtime: hasJsxRuntime ? "automatic" : "classic",
                    },
                  ],
                ],

                plugins: [
                  isEnvDevelopment & amp; & amp;
                  shouldUseReactRefresh & amp; & amp;
                  require.resolve("react-refresh/babel"),
                ].filter(Boolean),
                // This is a feature of `babel-loader` for webpack (not Babel itself).
                // It enables caching results in ./node_modules/.cache/babel-loader/
                // directory for faster rebuilds.
                cacheDirectory: true,
                // See #6846 for context on why cacheCompression is disabled
                cacheCompression: false,
                compact: isEnvProduction,
              },
            },

In fact, the idea is to write the compiled ts rules separately from other ones, and use ts-loader for ts.

Package and introduce another project for testing

We need to export this component because the packaged export is the index in the src directory, so change the name of index.tsx and add a reference to the exported component.
Add index.ts file:

import Gis from "./views/gis/Gis";
export {<!-- --> Gis };

Run the packaging command:

yarn build

As a digression here, start and build can run without adding run.
Copy the packaged file to another empty project, and introduce the plug-in in the empty project.
code show as below:

import React from 'react';
import './App.css';
import Gis from "../build/index.js"
function App() {<!-- -->
  return (
    <div className="App">
      <Gis />
    </div>
  );
}

export default App;

At this point the operation is successful.
To be continued~~
If there is an error report, just solve the problem according to the relevant error report.

npm release process

Log in to npm

Enter the command in the terminal:

npm login

Enter your username, password, and email in sequence
Some will also enter the verification code of their email address
Some will report an error as shown below:

The error message is as follows:

code F403
npm ERR!
npm ERR!403 403 Forbidden - PU https://registry.npmirror. con/-/user/org. couchdb.user:csiyu - [FORBIDDEN] Public registration is not allowetnpm ERR!403 In most cases, you or one of your dependencies are requestingnpmERr!403 a package version that is forbidden by your security policy, ornpmERR! 403 on a server you do not have access to.
npmERR! A complete log of this run can be found in:npmERR!/Users/chensiyu/.npm/_ logs/2023-10 27T07 10 40 630Z-debug -0.log![Please add image description](https:/ /img-blog.csdnimg.cn/b540f3eca1d3407ba8dc1734f3bddd69.jpeg)

The reason for the error is because of the npm mirror source. Just switch the mirror source.
Enter the command in the terminal:

npm config set registry https://registry.npmjs.org/

Just log in again.

Publish

Enter the command in the terminal:

npm publish

If you encounter the following error:

npm ERR! code E403
npm ERR! 403 403 Forbidden - PUT https://registry.npmjs.org/test-xxx - You do not have permission to publish “test-xxx”. Are you logged in as the correct user?
npm ERR! 403 In most cases, you or one of your dependencies are requesting
npm ERR! 403 a package version that is forbidden by your security policy, or
npm ERR! 403 on a server you do not have access to.

It’s because the package name conflicts with npm. Just modify the package name and re-publish it.

The successful release is as shown below: