Project address: Diana

Document address: muyunyun.cn/diana/

The meaning of making wheels

Why build your own wheels when there are so many front-end libraries? In my opinion, I have the following views:

  • Strong customization, according to their own needs for the leading extension of development. You can help others if you’re not careful (like the React library);
  • Many popular libraries, just according to their API for use, in fact, these libraries contain a lot of knowledge, skills, the best way is to imitate them to write small demo, so as to experience the essence of these libraries;
  • In the process of building wheels, I can feel the pleasure different from ordinary business development. For example, a big difference from daily business development is that there are more strict requirements for test cases; And the ability to write documents has improved.
  • So much for making this up…

Aside from the internal approach (it would be better to write a corresponding topic, so I’ll skip it here), here are a few things I learned while developing the Diana library:

Project directory structure

├ ─ ─ LICENSE open source licenses ├ ─ ─ the README - zh_en. Md English documentation ├ ─ ─ the README. Md Chinese documentation ├ ─ ─ coverage of code coverage files ├ ─ ─ docs document directory │ └ ─ ─ the static - parts │ ├ ─ ─ the index - end. Static HTML document directory at the end of file │ └ ─ ─ the index - start. Static HTML document directory beginning file ├ ─ ─ karma. Conf., js karma configuration file ├ ─ ─ lib │ ├ ─ ─ ├── package.json ├─ script │ ├─ build.js Build file │ ├─ check.js Pre-commit EsLint Check │ ├─ Tag-script.js Automatic File Tag │ ├─ Web-script.js Automatic file ├─ webPack.browse.js WebPack Configuration file │ └ ─ ─ webpack. Node. Js server-side webpack configuration file ├ ─ ─ snippets ├ ─ ─ the SRC │ ├ ─ ─ browser browser side method │ ├ ─ ─ common sharing method │ ├ ─ ─ the node to node │ ├─ ├─ tag_database ├─test├── ├─ ├─ ├.js ├─ ├─ ├.js ├─ ├.js ├── ├.js ├── ├.js ├── ├.js ├── ├.js ├── ├.jsCopy the code

The directory structure is also constantly iterating with the increase in methods, so it is recommended to go directly to the library to see the latest directory structure.

Accordingly, the method iterates over time, so it is recommended to review the documentation first, and click Ⓢ to view the source code.

Make modules run in Both Node.js and the browser

We can use the following methods to determine whether the module is currently running in Node.js or a browser, and then implement our functionality in different ways.

// Only Node.JS has a process variable that is of [[Class]] process
const isNode = Object.prototype.toString.call(typeofprocess ! = ='undefined' ? process : 0) = = ='[object process]'
Copy the code

However, if the user uses a module packaging tool, this will result in both node.js and the browser implementation being included in the final output file. In response to this, the open source community has proposed adding the Browser field to package.json, which is currently supported by WebPack and Rollup.

Provide the browser field with a file path as the module entry for use on the browser side, but note that the packaging tool will preferentially use the file path specified by the Browser field as the module entry, so your main and module fields are ignored. But this will cause the packaging tool not to optimize your code. Please refer to this question for more information.

In the Diana library, the following declaration is made in package.json in order to use appropriate files in different environments:

  "browser": "lib/diana.js"."main": "lib/diana.back.js".// 或者 "module": "lib/diana.back.js",
Copy the code

This way, in the Node environment, the lib/ dia.back.js file is referenced, and in the browser environment, the lib/ Dia.js file is referenced. Then you can happily use your own proprietary apis on both the browser side and the Node side.

Comparison of common module specifications

In addition, in order to make the packaging file of Diana library compatible with node side and browser side references, UMD specification was selected for packaging, so why choose UMD specification? Let’s look at the similarities and differences between the following specifications:

CommonJS

  • CommonJs is the specification for server-side modules, which Is adopted by Node.js. These specifications cover modules, binaries, buffers, character set encodings, I/O streams, process environments, file systems, sockets, unit tests, server gateway interfaces, packet management, and more.

  • According to the CommonJS specification, a single file is a module. The load module uses the require method, which reads a file and executes, finally returning the exports object inside the file.

  • CommonJS loads modules synchronously. For example, Node.js is mainly used for server programming, and the loaded module files are generally stored in the local hard disk, so the loading is relatively fast, without considering the asynchronous loading mode, so the CommonJS specification is more suitable. However, in a browser environment, to load modules from the server, this must be done in asynchronous mode. So there are AMD, CMD solutions.

AMD, CMD

  • AMD is the canonical product of RequireJS ‘module definition in the process of popularizing it. AMD believes in early execution.
 // AMD's default recommendation is
define(['./a'.'./b'].function(a, b) {
  a.doSomething()
  b.doSomething()
  ...
})
Copy the code
  • CMD is the normalized product of the SeaJS module definition during the promotion process. CMD advocates relying on proximity.
// CMD
define(function(require, exports, module) {
  var a = require('./a')
  a.doSomething()
  var b = require('./b')
  b.doSomething()
  ...
})
Copy the code

UMD

UMD is a combination of AMD and CommonJS. Because AMD is browser-based asynchronously loading modules and CommonJS is server-based asynchronously loading modules, they came up with another, more generic pattern, UMD, to solve the cross-platform problem.

The Diana library chose to output in umD mode. Let’s see what umD does:

(function (root, factory) {
  if (typeof exports === 'object' && typeof module= = ='object') { // UMD checks whether any node.js supported modules (exports) exist and uses CommonJS mode
    module.exports = factory()
  } else if (typeof define === 'function' && define.amd) { // Then check whether AMD is supported (define exists). If yes, use AMD mode to load modules.
    define([], factory)
  } else if (typeof exports === 'object') { // Another form of CommonJS
    exports['diana'] = factory()
  } else
    root['diana'] = factory() // Window}) (this.function() {
  return module
})
Copy the code

Test the pothole path

Code coverage

Unit test code coverage statistics are a good way to measure test cases. But high-quality code coverage checks are almost always necessary for online libraries. The test coverage of the Diana library is shown below.

You can see that coverage is divided into the following four types,

  • Line coverage: Is every line executed?
  • Function coverage: Is every function called?
  • Branch Coverage: Is every if block executed?
  • Statement coverage: Is every statement executed?

The coverage shown on Github is based on line coverage.

mocha + istanbul

In the initial release, only mocha was used to test the *.test.js file, and then codecov was used to get test coverage.

Is the karma

If only testing es5, ES6 syntax, in fact, using Mocha is enough, but involved in testing THE SYNTAX of Dom operations, etc., you must build a browser, on the test. What Karma does is automatically set up a browser environment for us to test.

In order for the browser to support the common.js specification, karma + Browserify was used in the middle, and although the test cases worked, the final code coverage file only had reference paths for each method. Finally, we come back to Karma + webpack, where we have a pit where we can package the compiled JS code coverage. After a few holes, we can finally see the coverage of the compiled code. Figure is as follows:

In this diagram, we can clearly see the number of times the test case runs through the lines of code in the source code (the number on the left), as well as the code not covered by the test case (shown in red in the figure). We can then improve the corresponding test cases to improve test coverage.

The core parts of the configuration file are as follows:

module.exports = function(config) {
  config.set({
    files: ['test/index.js'].// The file to load into the browser
    preprocessors: { / / pretreatment
      'test/index.js': ['webpack'.'coverage']},webpack: {
      module: {
        rules: [{
          test: /\.js$/.use: { loader: 'sourcemap-istanbul-instrumenter-loader' }, 22instrument-loader = 22instrument-loader; 22instrument-loader = 22instrument-loader; 22instrument-loader = 22instrument-loader
          exclude: [/node_modules/, /\.spec.js$/],
        }],
      }
    },
    coverageReporter: {
      type: 'lcov'.// It seems that only this type of reading is supported
      dir: 'coverage/'
    },
    remapIstanbulReporter: { // Generate the coverage file
      reports: {
        'text-summary': null.json: 'coverage/coverage.json'.lcovonly: 'coverage/lcov.info'.html: 'coverage/html/',}},reporters: ['progress'.'karma-remap-istanbul'].// remap-isbanbul also comments line 169 of the remap-istanbul package's CoverageTransformer. Js file with a sourcemap not found error. (heart tired). })}Copy the code

conclusion

This paper summarizes the significance of Diana library for wheel building, module compatibility and test cases. More on the library process automation and performance will be shared later. Underscore, Outils, EC-do, 30-seconds-of-code, etc. Underscore, Outils, EC-do, 30-seconds-of-code, etc.

Finally, welcome to make fun of issues.