In our daily work, sometimes we need an HTTP service to access our static resources to test the situation, and sometimes we need to find a file in a huge folder, layer by layer is still quite troublesome. Of course, there are many similar solutions in the market, such as building NGINX local services to access static resources, including HTTP-server, etc
Address demand pain points
- Implement a static resource server
- Enable services in any folder and visual interface to access internal resources
Building engineering
In making, for example
Remember to check the following three options, it will automatically initialize several configuration files, save yourself typing
Pull the project locally and start configuring ESLint and Husky
Code specification
With the development of the front-end more specification, the size of the project will be more and more large, developers involved more and more, a project collaboration scenarios also more and more, the code of the specification is a big problem, the emphasis on oral is not enough, need to have a set of specifications to constraint, save labor costs, efficiency is higher.
EditorConfig + Prettier + ESlint
- Solve the problem of poor readability and maintainability caused by non-standard code between teams
- Resolve code specification inconsistency caused by different editors used by team members
- Send out code style problems in advance, give corresponding hints, timely repair
- Reduce the number of iterations in the code review process and save time
- Automatic formatting, uniform code style
Integrate the EditorConfig configuration
EditorConfig helps maintain a consistent code style for multiple developers working on the same project on different IDE editors.
Website: editorconfig.org
Create the.editorConfig file at the root of the project:
# Editor configuration, see http://editorconfig.org# indicates the top-level EditorConfig configuration file root =true[*] # indicates that all files apply charset = utf-8Set character set to UTF -8Indent_style = space # indented style (TAB | space) indent_size =2# # end_of_line indentation size = lf control line type (lf | cr | CRLF) trim_trailing_whitespace =trueInsert_final_newline =true# Always insert a new line at the end of the file [*.md] # indicates that only md files apply the following rule max_line_length = off TRIM_trailing_whitespace =false
Copy the code
Note:
- VSCode to use EditorConfig requires going to the plugin market to download the plugin
EditorConfig VS Code
- JetBrains (WebStorm, IntelliJ IDEA) can be configured directly using EditorConfig without additional plug-ins.
Integrating the Prettier configuration
Prettier Prettier is a powerful code formatting tool that supports JavaScript, Typescript, Css, Scss, Less, JSX, Angular, Vue, GraphQL, JSON, Markdown, etc. Prettier is a powerful code formatting tool that supports JavaScript, Typescript, Css, Scss, JSX, Vue, GraphQL, JSON, Markdown, etc. Is currently the most popular formatting tool.
Liverpoolfc.tv: prettier. IO /
-
Install the Prettier
npm i prettier -D
-
Example Create the Prettier configuration file
Prettier supports configuration files in various formats, such as. Json,. Yml, yaml, and. Js. Create the.prettierrc file in the root directory
-
Configuration. The prettierrc
In this project, we do the following simple configuration. For more information about configuration items, please refer to the official website
{
"useTabs": false."tabWidth": 2."printWidth": 100."singleQuote": true."trailingComma": "none"."bracketSpacing": true."semi": false
}
Copy the code
- After Prettier is installed and configured, she can format code using commands
# Format all files (. Means all files) NPX prettier --writeCopy the code
Note:
- The VSCode editor Prettier requires downloading the plug-in Prettier-code Formatter to use Prettier for configuration
- JetBrains (WebStorm, IntelliJ IDEA) can be configured directly using EditorConfig without additional plug-ins.
Prettier when editing an editor such as VSCode or WebStorm, Prettier is formatted according to the format of the configuration file Prettier, avoiding code styles where editing tools differ from Prettier.
Integrate ESlint configuration
ESLint is a tool for finding and reporting problems in code, and for auto-fixing some problems. Its core is the ability to analyze code to check code quality and style problems through pattern matching of AST (Abstract Syntax Tree) obtained from code parsing.
As we mentioned before as team members programming ability and coding habits between different quality problems caused by the code, we use ESLint to solve, while writing code to find problems, if found wrong, prompt is given rules, and automatically repair, go down for a long time, can make team members gravitate to the same kind of coding style.
-
Install ESLint
The installation can be global or local, and the authors recommend local installation (only in the current project).
npm i eslint -D
-
Configuration ESLint
After ESLint is installed successfully, execute NPX ESLint –init and follow terminal instructions to complete a series of Settings to create the configuration file.
The plug-in Airbnb JavaScript Style Guide
JavaScript Standard Style
Google JavaScript Style Guide
Operation:
- How would you like to use ESLint? (How would you like to use ESLint?)
Here we select To check syntax, find problems, and Enforce code style.
- What type of modules does your project use? (What type of modules do you use for your project?)
We’re going to select JavaScript modules (import/export)
- Which framework does your project use? (Which framework do you use for your project?)
We choose vue.js here
- Does your project use TypeScript? (Does your project use TypeScript?)
So let’s say Yes
- Where does your code run? (Where does your code run?)
Here we select Browser and Node (press the space bar to select, then press Enter to confirm)
- How would you like to define a style for your project? (How would you like to define style for your project?)
Use a popular style guide
- Which style guide do you want to follow? (Which style guide would you like to follow?)
Here we choose Airbnb: github.com/airbnb/java…
ESLint lists three community-popular JavaScript style guides for us: Airbnb, Standard, and Google.
All three style guides are good enough to be used by companies large and small around the world, based on years of development experience. We chose the Airbnb with the most stars on GitHub to avoid the hassle of configuring ESLint rules, and then let the team learn the Airbnb JavaScript style guide.
At this point, we’ve configured Airbnb JavaScript rules in ESLint so that any code that doesn’t conform to Airbnb’s style will be flagged by the editor and fixed automatically when coding.
The authors do not recommend that you configure ESLint rules freely, and trust me, these three JavaScript code style guides are worth learning over and over again to improve your programming skills.
- The authors do not recommend that you configure ESLint rules freely, and trust me, these three JavaScript code style guides are worth learning over and over again to improve your programming skills.
Let’s choose JavaScript here
- Would you like to install them now with npm? (Would you like to install them with NPM now?)
Using the above option, ESLint will automatically look for missing dependencies, so here we select Yes and use NPM to download and install these dependency packages.
Note: If the automatic installation dependency fails, manual installation is required
npm i @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-airbnb-base eslint-plugin-import eslint-plugin-vue -D
Copy the code
- ESlint configuration files
.eslintrc.js
After the previous step, the.eslintrc.js configuration file is automatically generated in the project root directory:
module.exports = {
env: {
browser: true.es2021: true.node: true,},extends: ["plugin:vue/essential"."airbnb-base"].parserOptions: {
ecmaVersion: 12.parser: "@typescript-eslint/parser".sourceType: "module",},plugins: ["vue"."@typescript-eslint"].rules: {}};Copy the code
Depending on the project, if we have additional ESLint rules, we will append them to this file.
Note:
- To use the ESLint configuration file for VSCode, you need to download the plugin ESLint from the plugin market.
- JetBrains (WebStorm, IntelliJ IDEA, etc.) does not require additional plugins.
After the configuration is complete, we enable ESLin in an editor such as VSCode or WebStorm. When writing code, ESLint will check the code in real time according to the rules we configured, and will give error messages and fix solutions if any problems are found.
As shown in figure:
- VScode
- WebStorm
Although, now the editor has given error prompts and fixes, but we need to click one by one to fix, or quite troublesome. As simple as that, we simply set the editor to automatically execute the esLint –fix command for code style fixes when saving files.
- VSCode adds the following code to settings.json:
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
Copy the code
- WebStorm opens the Settings window, do as follows, and finally hit Apply -> OK.
8. Resolve conflict between Prettier and ESLint
When you add extra configuration rules for ESLint and Prettier to your project, there are often rules that conflict.
The ESLint configuration in this project uses the Airbnb JavaScript style guide validation. One of the rules is to put a semicolon after the end of code, while we added an unsemicolon after the end of code in the Prettier configuration file, so there’s a conflict, Prettier formatting code occurs after Prettier is used, ESLint detects formatting errors and throws an error. Eslint-plugin-prettier and eslint-config-prettier are used to solve conflicts between the two.
eslint-plugin-prettier
Prettier’s rule into ESLint’s ruleeslint-config-prettier
Disable the rule in ESLint that conflicts with Prettier.
Prettier Configuration Rule > ESLint Configates rules.
- Installing a plug-in
npm i eslint-plugin-prettier eslint-config-prettier -D
Copy the code
- Add the prettier plugin to.eslintrc.js
module.exports = {
...
extends: [
'plugin:vue/essential'.'airbnb-base'.'plugin:prettier/recommended' // Add the prettier plug-in],... }Copy the code
When esLint –fix was executed, ESLint would format code according to Prettier’s configuration, easily resolving the conflict between the two.
Integrate Husky and Lint-staged
We already integrate ESLint and Prettier into the project, where they verify code in real time and, to a certain extent, regulate it, but some people on the team may find the restrictions cumbersome and choose to ignore “hints”. Write your code in your own style, or disable the tools altogether, and commit your code directly to the repository. Over time, ESLint becomes useless.
So, we also need to make certain that the repository code is spec compliant by making it impossible to commit code that has not passed ESLint detection and fixes.
To solve this problem, you need to use a Git Hook. When a local Git commit is performed, ESLint checks and fixes the committed code (i.e., ESLint –fix). If the code fails to pass the ESLint rule, the commit is disabled.
To achieve this feature, we resorted to Husky + Lint-staged.
Husky — Git Hook, which can be set to trigger Git commands at various stages (pre-commit, commit-msg, pre-push, etc.).
Lint-staged — Run linters on files temporarily stored in Git.
Configuration husky
- Automatic configuration (recommended)
Use the husky-init command to quickly initialize a husky configuration in your project.
npx husky-init && npm install
This command does four things:
- The installation
husky
To development dependencies
2. Create the project in the root directory.husky
directory
In 3..husky
The directory to createpre-commit hook
And initializepre-commit
Command tonpm test
4. Modifypackage.json
Scripts, added"prepare": "husky install"
Here, husky is configured and now we can use it:
Husky contains many hooks, such as pre-commit, commit-msg, and pre-push. Here, we use pre-commit to trigger the ESLint command.
Alter.husky/pre-commit hook trigger command:
eslint --fix ./src --ext .vue,.js,.ts
Copy the code
The above pre-commit hook file does the following: Git commit -m “XXX” : $git commit -m “XXX” : $git commit -m “XXX” : $git commit -m
However, there is a problem: sometimes we need to implement ESLint –fix for all files when we have changed only one or two files. If this were a history project and we configured ESLint rules halfway through, then other unmodified “history” files would also be checked when submitting code, which could result in a large number of ESLint errors, obviously not the desired result.
We want to use ESLint only to fix the code we wrote this time and not affect the rest of the code. So there’s a magical tool called Lint-Staged.
Configuration lint – staged
Lint-staged this tool, commonly used in conjunction with Husky, allows Husky hook triggered commands to only act on Git add files (files in git staging) without affecting other files.
Next, we continued optimizing the project using Lint-staged.
- Install the lint – staged
npm i lint-staged -D
Copy the code
- in
package.json
Increase in thelint-staged
Configuration items
"lint-staged": {
"*.{vue,js,ts}": "eslint --fix"
},
Copy the code
Run eslint –fix only for.vue,.js,.ts files in git staging.
- Modify the
.husky/pre-commit
The trigger command of hook is:npx lint-staged
Husky and Lint-staged configurations are now complete.
Now when we submit code, it looks like this:
Add./ SRC/git commit -m “test…” Eslint –fix will be executed for only the two files test-1.js and test-2.ts. If ESLint passes, the commit succeeds, otherwise the commit terminates. This ensures that the code we submit to the Git repository is canonical.
- Before submission
test-1.js
,test-2.ts
- After submission
test-1.js
,test-2.ts
Automatically fix the code format
No matter writing code or doing other things, you should take a long-term view. At the beginning of using ESint, there may be a lot of problems, and it is very time-consuming and laborious to change. As long as you stick to it, the code quality and development efficiency will be improved, and the early efforts are worth it.
These tools are not necessary, and you can develop features without them, but you can write higher-quality code with these tools. In particular, some people who are new to these tools may feel troublesome and give up using them, missing a good opportunity to improve their programming skills.
Breakpoint debugging
Click on theCreate the launch.json file
Vscode /launch.json will be generated in the root directory
The following two debugging modes are introduced:
attach
"scripts": {
...
"debug": "node --inspect-brk ./src/app.js",
...
}
Copy the code
Perform yarn debug
Chrome devtools
Fill in port 9229:
You can then see that Chrome has scanned the target, and click Inspect to connect to the Debugger Server.
attach
{"name": "Attach", "port": 9229, // replace inspect-brk service port" request": "Attach", "skipFiles": [ "<node_internals>/**" ], "type": "pwa-node" },Copy the code
launch
Start the debugger server via node –inspect-brk and then add vscode debug to connect to the server
There are ways to do this, just add a launch configuration:
Program can modify the debug path, and you can set stopOnEntry to stop on the first line
More debugging details can be found at mp.weixin.qq.com/s/-Hz4SkAp9…
At this point, our basic configuration is complete and we can start the formal coding
Create an HTTP server
const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) = > {
res.statusCode = 200;
res.setHeader('Content-Type'.'text/plain');
res.end('Hello, World! \n');
});
server.listen(port, hostname, () = > {
console.log(`Server running at http://${hostname}:${port}/ `);
});
Copy the code
One Hello World
Of course, this is just a demo, but in order to achieve our goal, we need to deal with req, RES, which is the request parameters, and RES, which is the response parameters
Unified encapsulates a method to deal with
route(req, res, filePath, config)
Copy the code
const fs = require('fs')
// Asynchronous processing
const { promisify } = require('util')
// Template engine
const handlebars = require('handlebars')
// read the file stream in segments
const range = require('./range')
// mimeType
const mime = require('./mime')
const path = require('path')
// Asynchronous processing
const stat = promisify(fs.stat)
const readdir = promisify(fs.readdir)
// Splice the template path
const tplPath = path.join(__dirname, '.. /template/dir.tpl')
// Template file stream
const source = fs.readFileSync(tplPath)
// Handlebars engine processing
const template = handlebars.compile(source.toString())
// req Request body res Response content filePath Absolute path of the current file, config to obtain a root path
module.exports = async function (req, res, filePath, config) {
try {
// Read files asynchronously
const stats = await stat(filePath)
// Check whether it is a file
if (stats.isFile()) {
// Dynamically read the extension of the current file and get the corresponding mimeType
const contentType = mime(filePath)
// Set the response header
res.setHeader('Content-Type', contentType)
// Read the file stream
let rs
const { code, start, end } = range(stats.size, req, res)
if (code === 200) {
res.statusCode = 200
rs = fs.createReadStream(filePath)
} else {
res.statusCode = 206
rs = fs.createReadStream(filePath, { start, end })
}
rs.pipe(res)
} else if (stats.isDirectory()) {
// Read the list of files under the folder
const files = await readdir(filePath)
res.statusCode = 200
res.setHeader('Content-Type'.'text/html')
// relative(path.relative(from, to)
//
const dir = path.relative(config.root, filePath)
// Data passed to the template file
const data = {
title: path.basename(filePath),
dir: dir ? ` /${dir}` : ' '.files: files.map((file) = > {
return {
file,
icon: mime(file)
}
})
}
res.end(template(data))
}
} catch (error) {
res.statusCode = 404
res.setHeader('Content-Type'.'text/plain')
res.end(`${filePath} is not a diretory or file \n ${error.toString()}`)
console.error(error)
}
}
Copy the code
1. Handle promisify asynchronously
const stat = promisify(fs.stat)
const readdir = promisify(fs.readdir)
const stats = await stat(filePath)
Copy the code
2. Template engines
const handlebars = require('handlebars')
// Splice the template path
const tplPath = path.join(__dirname, '.. /template/dir.tpl')
// Template file stream
const source = fs.readFileSync(tplPath)
// Handlebars engine processing
const template = handlebars.compile(source.toString())
// Data passed to the template file
const data = {
title: path.basename(filePath),
dir: dir ? ` /${dir}` : ' '.files: files.map((file) = > { return { file, icon: mime(file) } })
}
res.end(template(data))
Copy the code
When the server responds to a resource to the front end, a content-Type is specified and the browser parses the rendered Content in the corresponding format
Let ext = path.extName (filePath).split(‘.’).pop().tolowerCase (), path.extName gets the extension and maps a mimeType value to content-Type
4. Range Reads resources in segments
module.exports = (totalSize, req, res) = > {
const { range } = req.headers
if(! range) {return {
code: 200}}const sizes = range.match(/bytes=(\d*)-(\d*)/)
const end = sizes[2] || totalSize - 1
const start = sizes[1] || totalSize - end
if (start > end || start < 0 || end > totalSize) {
return {
code: 200
}
}
res.setHeader('Accept-Ranges'.'bytes')
res.setHeader('Content-Range'.`bytes ${start}-${end}/${totalSize}`)
res.setHeader('Content-Length', end - start)
return {
code: 206.start: global.parseInt(start),
end: global.parseInt(end)
}
}
Copy the code
let rs
const { code, start, end } = range(stats.size, req, res)
if (code === 200) {
res.statusCode = 200
rs = fs.createReadStream(filePath)
} else {
res.statusCode = 206
rs = fs.createReadStream(filePath, { start, end })
}
Copy the code
5, resource compression (mainly aimed at two forms: gizp | deflate)
const { createGzip, createDeflate } = require('zlib')
module.exports = (rs, req, res) = > {
const acceptEncoding = req.headers['accept-encoding']
if(! acceptEncoding || ! acceptEncoding.match(/\b(gizp|deflate)\b/)) {
return rs
}
if (acceptEncoding.match(/\bgizp\b/)) {
res.setHeader('Content-Encoding'.'gzip')
return rs.pipe(createGzip())
}
if (acceptEncoding.match(/\bdeflate\b/)) {
res.setHeader('Content-Encoding'.'deflate')
return rs.pipe(createDeflate())
}
return false
}
Copy the code
rs = compress(rs, req, res)
Copy the code
6, caching,
const { cache } = require('.. /config/defaultConfig')
function refreshRes(stats, res) {
const { maxAge, expires, cacheControl, lastModified } = cache
if (expires) {
res.setHeader('Expires'.new Date(Date.now() + maxAge * 1000).toUTCString())
}
if (cacheControl) {
res.setHeader('Cache-Control'.`public, max-age=${maxAge}`)}if (lastModified) {
res.setHeader('Last-Modified', stats.mtime.toUTCString())
}
// if (etag) {
// res.setHeader('Etag', `${stats.size}-${stats.mtime}`)
// }
}
module.exports = function isFresh(stats, req, res) {
refreshRes(stats, res)
const lastModified = req.headers['if-modified-since']
const etag = req.headers['if-none-match']
if(! lastModified && ! etag) {return false
}
if(lastModified && lastModified ! == res.getHeader('Last-Modified')) {
return false
}
// if (etag && etag ! == res.getHeader('Etag')) {
// return false
// }
return true
}
Copy the code
chmod
Bin /index -p port number bin/index -p port number bin/index can be executed if an error is reported.
-rwxr-xr-x
First representative: D (folder) – (file)
RWX corresponds to read (write) and execute (execute) permissions
Learn more about chmod: www.runoob.com/linux/linux…
Release NPM
Create bin/dserver from the root directory
#! /usr/bin/env node
require('.. /src/index')
Copy the code
package.json
"bin": {
"dserver": "bin/dserver" // The "dserver" key is the command example for using the NPM package dserver -p 9999
},
Copy the code
Since app.js is automatic, it needs to be wrapped. The class encapsulates app.js
The parameters of processing yargs start projects, in particular, using reference as follows, more detailed document reference: www.npmjs.com/package/yar…
const yargs = require('yargs')
const Server = require('./app')
const { argv } = yargs
.usage('anywhere [options]')
.option('p', { alias: 'port'.describe: 'Port number'.default: 9527 })
.option('h', {
alias: 'hostname'.describe: 'host'.default: '127.0.0.1'
})
.option('d', {
alias: 'root'.describe: 'root path'.default: process.cwd()
})
.version()
.alias('v'.'version')
.help()
console.log(argv)
const server = new Server(argv)
server.start()
Copy the code
Once configured, NPM can be published and NPM publish is OK
If your release doesn't work, check out the following possibilities. Don't lose heart when you run into difficulties. Check out this checklist: 1. Check whether you have logged in to NPM (NPM whoami) 2. Check whether the email address you filled in when registering is verified. If the page is not refreshed, click the notification bar at the top to resend the verification email 4. Check if your package name is already in use 5. Check if your version number is already in use 6. Check if your NPM source is an official NPM source (NPM config list)Copy the code
Write in the last
Making: github.com/Spring-List…