“This is my fourth day of participating in the First Challenge 2022. For more details: First Challenge 2022.”

Written in the beginning

We talked about how adding a new component goes through three steps:

  • Step 1 – Create the component directory structure
  • Step 2 – Create the component style file
  • Step 3 – General entry file import component

And we realized the second step of automatic creation operation, that is, only through the command to complete the creation of the file, no manual creation operation. Of course, this second step is not advanced enough, this chapter will take you to automate the first step and absorb the results of the second step, making it more advanced as a whole.

Pre-knowledge

The process module

The Process module is a global object in the Node environment. It is similar to the Window object in the browser environment, except that it exists in the Node environment. It has many methods and properties.

Create a test.js file in the root directory of your project with the following contents:

// test.js console.log(process.argv); Process. On (' exit '() = > {the console. The log (' command line out of the'); });Copy the code

Execute this script file:

It’s enough to know from the screenshot above that process.argv can get some arguments from the command line input. If you are interested in the Process module, you can learn it by yourself.

File – save the module

The file-save module, which we’ll use later, is similar to our last article’s fs.writefilesync (‘ file path ‘, ‘file content ‘,’ file encoding/error callback ‘); Similarly, it is used to create and write files.

Download the package: NPM install [email protected] -d

Basic use:

// test.js const fileSave = require('file-save'); fileSave('./1.js') .write(`var a = 1; `, 'utf8') .write(`var b = 2; `, () = > {the console. The log (' write callback ')}). The end (' \ n '). Finish (() = > {the console. The log (' write complete '); });Copy the code

If the directory does not exist, the module will create its own directory, which is actually a layer of writing encapsulation, to achieve a segmentalized chain call, more convenient and quick to use.

Uppercamelcase module

The upperCamelCase module converts dashes/dots/underscores/Spaces separated strings to hump form.

Download the package: NPM install [email protected] -d

Basic use:

// test.js
const uppercamelcase = require('uppercamelcase');
const ComponentName = uppercamelcase('el-button'); // ElButton
const ComponentName = uppercamelcase('button'); // Button
Copy the code

Json.stringify () takes the third argument

The json.stringify (value[, replacer [, space]]) method is familiar, but you probably don’t notice its other two parameters when you use it.

Let’s take a look at three examples:

Var obj = {name: 'coding', age: 18, hobby: ['coding', 'writing']};Copy the code

With only one argument, the object becomes a string, so I won’t go into that.

console.log(JSON.stringify(obj)); / / {" name ":" orange someone ", "age" : 18, "hobby" : [" coding ", "writing"]}Copy the code

When there is a second argument, the second argument can be an array or a function that filters a key-value pair.

console.log(JSON.stringify(obj, ['name', 'age'])); // {"name":" age":18}Copy the code

When there is a third argument, the third argument is used to control the spacing within the resulting string.

console.log(JSON.stringify(obj, null, '  '));
console.log(JSON.stringify(obj, null, '~'));
Copy the code

Automatically create the component directory structure

“Button” and “Divider” components. Their directory structures are basically the same, consisting of an index.js file and a SRC /main.vue file. And the contents of index.js are generally similar.

The goal of this section is to implement a directory structure that automatically creates components through commands.

First, let’s create the build/ SRC /new.js file.

Second, configure the script file with another command:

"new": "node build/bin/new.js"
Copy the code

Then let’s look at the contents of the new.js file below:

// Listen for command line exit process.on('exit', () => {console.log(' command line exit'); }); // NPM run new XXX must enter the component name, otherwise end the command if (! Process.argv [2]) {console.error('[component name] Mandatory - Please enter new component name'); process.exit(1); } // const path = require('path'); const fileSave = require('file-save'); Const ComponentName = process.argv[2]; const ComponentName = process.argv[2]; Const PackagePath = path.resolve(__dirname, '.. /.. /packages', componentname); // Define the file to be created const Files = [{filename: 'index.js', content: '',}, {filename: 'SRC /main.vue', content: '',}]; ForEach (file => {fileSave(path.join(PackagePath, file.filename)).write(file.content, 'utf8') .end('\n'); });Copy the code

The content of the script file should not be difficult, which is to create the required Files according to our defined Files array. Run the NPM run new input command and you can see that the directory for the component is automatically created.

However, these two Files are still empty Files, but we reserved a content field in Files to fill them, but the key is how to fill this content? This is up to you to play with, this is a bit like we normally configure the editor to quickly create a file prefill template.

. Const uppercamelcase = require('uppercamelcase'); . ElButton: ElButton const ComponentName = upperCamelCase (ComponentName); const Files = [ { filename: 'index.js', content: ` import ${ComponentName} from './src/main.vue';  /* istanbul ignore next */ ${ComponentName}.install = function(Vue) { Vue.component(${ComponentName}.name, ${ComponentName}); }; export default ${ComponentName}; `, }, { filename: 'src/main.vue', content: ` <template> <div class="el-${componentname}">${componentname}</div> </template> <script> export default { name: 'El${ComponentName}' }; </script> `, } ]; .Copy the code

We’ll simply take the template reserved for ElementUI source code, execute NPM run new Input again, and you’ll have the file content generated. (The file content format can be adjusted by itself, here is some newline processing for browsing effect)

Automatically generate components. Json files

In the previous article ElementUI source code Analysis series – learning about the gen-cssfile.js file for automatically creating component.scss files and generating index. SCSS files we learned that executing the NPM run gen command can help us quickly create component.scss Style file, but only if you define the component in the components.json file.

// components.json
{
  "button": "./packages/button/index.js",
  "divider": "./packages/divider/index.js",
  "loading": "./packages/loading/index.js",
  "alert": "./packages/alert/index.js",
}
Copy the code

NPM run new XXX command is used to create a new component. The.scss file of the new component is defined in the components.json file and then automatically created by the NPM run gen command. This is obviously cumbersome and unreliable…

How about after we create a new component, we add the definition of the new component to the components.json file? The answer, of course, is yes.

Let’s look at how to modify the new.js file:

Process. On (' exit '() = > {the console. The log (' command line out of the'); }); if (! Process.argv [2]) {console.error('[component name] Mandatory - Please enter new component name'); process.exit(1); } const path = require('path'); const fileSave = require('file-save'); const uppercamelcase = require('uppercamelcase'); const componentname = process.argv[2]; const PackagePath = path.resolve(__dirname, '.. /.. /packages', componentname); const ComponentName = uppercamelcase(componentname); Const componentsFile = require('.. /.. /components.json'); If (componentsFile[ComponentName]) {console.error(' ${componentName} exists. '); process.exit(1); // If you already have the new component definition in the components.json file, exit the command line directly. ComponentsFile [ComponentName] = './packages/${componentName}/index.js'; FileSave (path.join(__dirname, '.. /.. /components.json')) .write(JSON.stringify(componentsFile, null, ' '), 'utf8') // json.stringify.end('\n'); const Files = [ { filename: 'index.js', content: '', }, { filename: 'src/main.vue', content: '', } ]; Files.forEach(file => { fileSave(path.join(PackagePath, file.filename)) .write(file.content, 'utf8') .end('\n'); });Copy the code

The content field of the Files variable, which I’ve omitted because it takes up too much space, actually adds seven or eight lines of code. You can run the NPM run new input command again to see if the components. Json file automatically generates the component definition.

At this point, we create a new component that can execute the NPM run new XXX and NPM run gen commands directly to do the basic creation. However, executing two commands in a row is not what we want in the end. We want one command to do everything.

Incorporate the automation of the second step

The expectation is that the NPM run new XXX command will automatically create the component directory structure and create the.scss file for the component, which will be automatically introduced in the index.scss master style file.

How do you do that? Create the component’s.scss file? Creating a file… ? Isn’t that easy? We defined the Files array variable, remember? Let’s add one more term to it and we’re done.

// new.js ... // Define the file to create const Files = [{filename: 'index.js', content: ",}, {filename: 'SRC /main.vue', content: '', }, { filename: path.join('../../packages/theme-chalk/src', `${componentname}.scss`), content: ` @import "mixins/mixins"; @import "common/var"; @include b(${componentname}) { } ` }, ]; .Copy the code

NPM run new XXX to see if there is a.scss file generated for the component.

By looking at the.scss file for each component of ElementUI, they all have a common structure that looks like the one below. You can add it to the.scss file, or change it as you like.

Now that we’ve created the index.scss file, we’re left with the problem we introduced in the index. SCSS file.

// new.js ... const fs = require('fs'); Const sassPath = path.join(__dirname, '.. /.. /packages/theme-chalk/src/index.scss'); Const sassImportText = '${fs.readFileSync(sassPath)}@import "./${componentName}.scss"; `; FileSave (sassPath).write(sassImportText, 'utf8').end('\n'); const Files = [ { filename: 'index.js', content: '', }, { filename: 'src/main.vue', content: '', }, { filename: path.join('../../packages/theme-chalk/src', `${componentname}.scss`), content: '' }, ]; .Copy the code

Add four more lines of code, and eventually you’ll have to run the NPM run new XXX test to see if it works.

We ended up with a command called NPM run new XXX to create the component, and we were happy to be done.

New.js complete source code

Process. On (' exit '() = > {the console. The log (' command line out of the'); }); if (! Process.argv [2]) {console.error('[component name] Mandatory - Please enter new component name'); process.exit(1); } const path = require('path'); const fs = require('fs'); const fileSave = require('file-save'); const uppercamelcase = require('uppercamelcase'); const componentname = process.argv[2]; const PackagePath = path.resolve(__dirname, '.. /.. /packages', componentname); const ComponentName = uppercamelcase(componentname); // components.json const componentsFile = require('.. /.. /components.json'); If (componentsFile[ComponentName]) {console.error(' ${componentName} exists. '); process.exit(1); } componentsFile[componentname] = `./packages/${componentname}/index.js`; fileSave(path.join(__dirname, '.. /.. /components.json')) .write(JSON.stringify(componentsFile, null, ' '), 'utf8') .end('\n'); // index.scss const sassPath = path.join(__dirname, '.. /.. /packages/theme-chalk/src/index.scss'); const sassImportText = `${fs.readFileSync(sassPath)}@import "./${componentname}.scss"; `; fileSave(sassPath) .write(sassImportText, 'utf8') .end('\n'); const Files = [ { filename: 'index.js', content: `import ${ComponentName} from './src/main';  /* istanbul ignore next */ ${ComponentName}.install = function(Vue) { Vue.component(${ComponentName}.name, ${ComponentName}); }; export default ${ComponentName};`, }, { filename: 'src/main.vue', content: `<template> <div class="el-${componentname}"></div> </template> <script> export default { name: 'El${ComponentName}' }; </script>`, }, { filename: path.join('../../packages/theme-chalk/src', `${componentname}.scss`), content: `@import "mixins/mixins"; @import "common/var"; @include b(${componentname}) { }` }, ]; Files.forEach(file => { fileSave(path.join(PackagePath, file.filename)) .write(file.content, 'utf8') .end('\n'); });Copy the code





Content of the past

  • ElementUI source code series 1 – Building project architecture from scratch, project preparation, project packaging, project testing process
  • ElementUI source code series 2 – introducing SCSS, using GULp to convert SCSS to CSS and complete and compress, using CP – CLI to move directories and files
  • ElementUI source code series 3 – Learn the gen-cssfile.js file for automatically creating component. SCSS files and generating the contents of index.scss files
  • ElementUI source code series 4 – Learn how to automatically create component directory structure and generate components. Json file contents in new.js
  • ElementUI source code series 5 – Learn the contents of the build-entry.js file that automatically generates the master entry file index.js
  • ElementUI source code series six – summary
  • ElementUI source code series 7 – Components introduced on demand




At this point, this article is finished, sa Hua Sa hua.

I hope this article has been helpful to you. If you have any questions, I am looking forward to your comments. Same old, likes + comments = you know it, favorites = you know it.