Background: As development teams continue to grow in size, collaboration costs increase along with more people, and business projects become more and more diverse. Common types are component classes, activity classes, React+ Redux based business projects, RN projects, Node.js projects, and so on. It would be hard to impose some discipline on every project, such as Git commit specifications and Javascript specifications. All because of the lack of a good engineering tool. From project creation, development, construction, code specification inspection to final project launch, CLI can improve efficiency and ensure the implementation of development specifications.
Node.js implements the basic principle of CLI
The key is the bin field in package.json. For UNIx-like systems, create a soft link in /usr/local/bin. For Windows, create a soft link in the C: Users\username\AppData\Roaming\ NPM directory. /node_modules/.bin directory in the project./node_modules/.bin directory.
The life cycle of a modern Web project
With the continuous evolution of front-end engineering, on the one hand, the engineering becomes more and more complex, and on the other hand, the demand for specification and quality is increasing. A modern Web project should consist of the following phases: initialization, development, construction, review, and release. As shown below:
Pain point 1: Project copy
The problems of project copy are obvious in the following three aspects:
- Prone to error; Once a copy of a critical file is lost or faulty, it can take half a day to a day to troubleshoot environmental problems.
- Different scenarios have different requirements on directory structure. During the normal development process, projects are usually divided into operations activities, Hybrid businesses, and entry level projects with extreme and demanding requirements for performance and experience. Need to be based on RN or Node.js first screen straight out, as well as common business components and other development.
- New Feature and BugFix are difficult to synchronize; New methods added or bugs solved by one student in the development process can hardly be passed on to other students and accumulated as experience.
The community offers the perfect Yeoman solution for the creation of automation projects. The Yeoman creation project consists of the following phases:
- Initializing: Initializing some state or other, usually with options or arguments entered by the user
- Only the latest category of targets is chosen by insurgents: Chosen when interacting with a user (command line q&A, etc.)
- Configuring: Saving configuration files (e.g..babelrc etc.)
- Writing: Generates template files
- Install: Installs dependencies
- End: The end part, the initial code is automatically committed
We just need to inherit Yeoman’s Generator class for template customization, and yeoman-based scaffolding design ideas should look like the following:
First, the developer interacts with the CLI. The developer tells the CLI what type of project to create, and the CLI receives the command. Select some type of template from the Yeoman scaffolding already installed locally. The CLI then calls the Gitlab API to create the repository remotely and grant the developer master privileges. Next, the system automatically applies for some information based on actual service scenarios, such as offline package IDS and monitoring alarm ids. After that, the code is generated in the local directory and the NPM package that the project depends on is installed. Finally, all the code generated from this initialization is automatically committed to the remote Git repository.
Pain point 2: The operation configuration is frequently modified
In the React+ Redux component-based development mode, a page or WebApp is rendered by assembling multiple container components.
A component usually consists of: templates, CGI data, and events. Ideally, development and production coexist peacefully, and you can write a component like this, such as a rule component:
render() {
return (
<div className="lottery-rule">
<div className="section"> < H3 > Activity time: </ H3 > <p> September 14 ~ September 30 </p> </div> <div className="section"> < H3 > Rules: </ H3 > <p> </p> <p>2. Rank according to the number of "likes" of the video. </p> <p>3, according to the city selection, respectively "tomorrow's son" (male participation only) and "shining goddess" only female participation. </p> </div> </div> ); }Copy the code
At first glance, there’s nothing wrong with that. In fact, it is likely to be 7 or 8 times of copy modification, even after the opening of the external entrance, still need to modify the copy or static data such as pictures. Then, you need to go through the code release process.
A better solution is to use past experience to analyze which static data is likely to require frequent changes before developing a business component. It is not necessary to pull out the static data that is highly modified for ten thousand years. So how do you make static data dynamic?
The answer is: Schema First, design the Schema before developing the component, and generate a form form from the Schema to achieve static data and template separation. If React is used, you can customize it based on React – jsonSchema-form. Static data separated from the template should look like this:
Pain point 3: Lack of collaboration specifications
This section uses Git commit specification as an example to introduce related improvements.
A good Git commit specification has the following advantages:
- Speed up the Review process
- Generate Changelog based on Commit metadata
- The maintainer can then know why the feature was added
Git Commit solution using the Google Angular project commit as a reference:
The specific submission format requirements are as follows:
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>Copy the code
The description of the format is as follows:
- Type represents the type of a commit, such as fixing a bug or adding a new feature. All types are as follows:
- Feat: Added feature
- Fix: fix the bug
- Docs: Only modified documents, such as README, CHANGELOG, CONTRIBUTE, etc
- Style: Just changed Spaces, formatting indentation, all good, etc., without changing the code logic
- Refactor: Code refactored without adding new features or fixing bugs
- Perf: Optimization related, such as improved performance, experience
- Test: test cases, including unit tests and integration tests
- Chore: Change the build process, or add dependency libraries, tools, etc
- Revert: Rollback to a previous version
One-click Generation of Changelog version logs:
Pain point 4: Lack of code specification
A bloody accident in production environment: On April 13, 2017, Tencent senior engineer Xiao Sheng modified apple IAP payment configuration by adding duplicate keys in JSON configuration when he was doing top-up business. After the code was released, a small part of users who used Vivo mobile phones reported that the recharge page was blank and they could not recharge in the Now APP. Finally, the problem was located as follows: Vivo mobile phone used webView of the system instead of X5 kernel, and repeated key errors were reported when parsing JSON, resulting in a blank screen.
Modern browsers are compatible with duplicate keys in JSON, but some older browsers, such as vivo phones, are not, causing code to fail directly. So how to avoid similar problems in the future?
ESLint is a code specification checking tool for Javascript and JSX. Compared to JSLint and JSHint, ESLint is more flexible and supports custom configurations, plug-in extensions, and configuration error levels. While adding ESLint adds a lot of code change costs to the team, the benefits certainly outweigh the costs in the long run.
The principles of the Javascript specification:
- Don’t duplicate wheels, based on ESLint: Recommend configuration and improved
- Enable all rules that help you find code errors
- Configuration should not depend on a specific project, but should be as rational as possible
- Helps keep the team’s code style consistent, rather than limiting the development experience
- There are corresponding explanatory documents
To better customize and maintain the Javascript specification, we created the Shareable Config for ESLint. On the one hand, we feel that some of the configuration definitions in ESLint: Recommend are too strict in their error levels, such as the presence of console in code causing validation errors, and on the other hand, it doesn’t include ESLint best practices and other rules. Some of the rules we defined are explained as follows:
Rule name | The error level | instructions |
---|---|---|
for-direction | error | The orientation of the for loop must be correct |
getter-return | error | A getter must have a return value, and undefined, such as return, is prohibited. |
no-await-in-loop | off | Allow await inside loops |
no-console | off | Allows console to be used in code |
no-prototype-builtins | warn | Calls methods directly on the object prototype chain |
valid-jsdoc | off | Function annotations must follow the JSdoc rules |
no-template-curly-in-string | warn | {and} appear in the string to warn |
accessor-pairs | warn | Getters and setters warn when they are not paired |
array-callback-return | error | For data-related operation functions such as reduce, map, filter, etc., callback must have a return |
block-scoped-var | error | Treat the var keyword as a block-level scope to prevent variable promotion bugs |
class-methods-use-this | error | A method that does not use this should be declared static |
complexity | off | Turn off code complexity limits |
default-case | error | The default branch must be required in the switch case statement |
ESLint implementations can be plugged into PUSH hooks as follows:
# 1, install the husky
$ npm install husky --save-dev
#2, integrated into NPM script
{
"scripts": {
"precommit": "validate-commit-msg"."prepush": "eslint src ./.eslintrc.js --ext '.js,.jsx'"}}Copy the code
CLI design
The CLI connects a series of pain points during project development to improve development efficiency and ensure the implementation of specifications.
Plug-in design
Plug-in implementation Principle
In a neat way, node provides module and VM modules to access cli instances by injecting feFlow global variables. This gives you access to various properties on the CLI, such as config, log, and some helpers.
loadPlugin(path, callback) {
const self = this;
return fs.readFile(path).then((script) => {
const module = new Module(path);
module.filename = path;
module.paths = Module._nodeModulePaths(path);
function require(path) {
return module.require(path);
}
require.resolve = function(request) {
return Module._resolveFilename(request, module);
};
require.main = process.mainModule;
require.extensions = Module._extensions;
require.cache = Module._cache;
// Inject feflow variable
script = '(function(exports, require, module, __filename, __dirname, feflow){' +
script + '}); ';
const fn = vm.runInThisContext(script, path);
return fn(module.exports, require, module, path, pathFn.dirname(path), self);
}).asCallback(callback);
}Copy the code
Command registration:
The command needs to be registered with feflow.cmd.register, for example:
feflow.cmd.register('deps'.'Config ivweb dependencies'.function(args) {
console.log(args);
// Plugin logic here.
});Copy the code
Description:
- Register has three parameters. The first parameter is the name of the subcommand, the second parameter is the description of the command, and the third parameter is the logical function executed by the corresponding subcommand.
- Feflow parses the command-line argument args into an Object that is passed to the plug-in handler
configuration
Feflow. version can be used to obtain the current version of feflow, feflow.baseDir can be used to obtain the feflow directory (.feflow in the user directory), and feflow.plugindir can be used to obtain the plug-in directory
The log
Run feflow.log to output command line logs
const log= feflow.log; Log.info () // Prompt logs, the console displays green log.debug() // debug logs, the command line adds --debug can be enabled, the console displays gray log.warn() // warning logs, Log.error () // Error logs are displayed on the console on a yellow background, and log.fatal() // Fatal error logs are displayed on the console in redCopy the code
The last
Thanks for the exchange opportunity provided by OSC to share and learn from developers. The NOW Live IVWEB team’s engineering solutions are as follows:
- Github homepage: github.com/cpselvis/fe…
- Code cloud home page: gitee.com/cpselvis/fe…
Attachment: PPT sharing this time