preface

Npm package – Nuggets (juejin. Cn) introduces the realization of automatic push warehouse && automatic update version and other functions. This talk about automatically generating template files, rollup loading package configurations on demand, and automatically generating directory lists is ridiculously simple!

Load the packaged configuration on demand

Loading on demand sounds fancy, but it’s essentially packing different module function files into multiple files, rather than all of them into one main file.

First create a rollup.config.js file, then download the required plug-in, and create the pluginscommon.js file to configure the common plug-in. The code is as follows:

const { getBabelOutputPlugin } = require('@rollup/plugin-babel')
// Is used to compile typescript into javascript
const typescript = require('rollup-plugin-typescript2')
const resolve = require('rollup-plugin-node-resolve')
const commonjs = require('@rollup/plugin-commonjs')
// For compression
const { terser } = require("rollup-plugin-terser")
module.exports = [
    typescript(),
    commonjs(),
    resolve(),
    getBabelOutputPlugin({
        presets: ['@babel/preset-env'].allowAllFormats: true,
    }),
    terser()
]
Copy the code

If you don’t know about plug-ins, please Google.

The rollup package runtime environment is nodeJs and follows the commonJS specification.

Rollup provides a variety of package formats for rollup. What I need is a medash.min.js file for CDN acceleration in UMD format and several CJS files to load on demand.

For those unfamiliar with UMD and CJS, check out the rollup package format.

The medash.min.js file is easy to get, just set the package entry file to main.ts and add the following to rollup.config.js:

/ /...
const pluginsCommon = require('./pluginsCommon')

export default [
/ /...
 {
    input: "./main.ts".output: [{file: 'dist/medash.min.js'.name: 'medash'.format: 'umd'}].plugins: pluginsCommon
}];
Copy the code

Loading the packaging configuration on demand is a bit trickier, I need to get the path of all the files that need to be packaged on demand. Writing files one by one is pretty cumbersome, so I created a build.js file to automatically get the path to the package file.

I define an array of variables named buildInputs to store all the file paths, and all the files that need to be packed are stored in the SRC directory.

Obtain all files or folders in the SRC directory and iterate through them one by one. If the file type is TS, the relative path to the file is pushed into the buildInputs array. If the file type is a folder, obtain the file directory under the folder and repeat the previous step.

To achieve the above, nodeJs FS module and PATH module need to be imported first:

const fs = require('fs');
const Path = require('path');
Copy the code

Fs.readdirsync (path) Retrieves folder directory files:

/ /...
function getFileNames(befter, after) {
    let filesPath = Path.join(befter, after)
    return { files: fs.readdirSync(filesPath), path: filesPath };
}
/ /...
Copy the code

Get the relative path to the file and push it to the buildInputs array:

/ /...
function setBuildInputs(tsFile) {
    let filePath = tsFile.split('src') [1]
    buildInputs.push('./src' + filePath.replace(/\\/g."/"))}/ /...
Copy the code

Pathe. extname(childrenPath) Retrieves the file name suffix and performs different logical processing for different suffix names:

/ /...
function startBuild({ files, path }) {
    if (files.length === 0) {
        return;
    }
    files.forEach((file) = > {
        let childrenPath = Path.join(path, file);
        let isExtname = Path.extname(childrenPath);
        if (isExtname === '.ts') {
            setBuildInputs(childrenPath);
        } else if (isExtname === '.js') {
            return;
        } else {
            startBuild(getFileNames(path, file));
        }
    })
}
startBuild(getFileNames(__dirname, '.. /src'));
/ /...
Copy the code

SRC directory residual. Js files, optional skip.

Effect demonstration:

Source code address: github.com/CatsAndMice…

Automatically generates template files

Can you help me solve any problems

  1. Every time I want to add a method file, I need to create four files, one for eachsrcUnder the filexxx.ts, test,testUnder folderxxx.test.ts, documents,docsUnder folderxxx.mdAs well asexampleUnder the filexxx.ts, I want to create it automatically;
  2. Increase the method needed inxxx.test.ts, casexxx.tsThe import andmain.tsFile import/export step, I want to do this step automatically.

Open lu

50+ lines of code with one line of command update Npm package – juejin.cn, mention inquirer. Similarly, the implementation of automatic template file generation starts with terminal interaction.

Create a create.ts file in the build folder.

When I want to add a method, the terminal should provide a directory selection so that the added file can be created in the corresponding directory. When I select the corresponding directory, the terminal provides an input box function for naming the file.

Directory selection code:

/...
import getSrcLists from "./getSrcLists";
/ /...
async function typesCheck() {
    const lists = await getSrcLists();
    inquirer.prompt([
        {
            name: 'list'.type: 'list'.message: 'Please select the corresponding folder'.choices: lists,
            default: [lists[0]]
        }
    ]).then(({ list }) = > {
        getName(list)
    })
}
/ /...
Copy the code

The getSrcLists method could be used to obtain the name of the SRC folder for directory selection. import fs from “fs/promises”; Export fs/ Promises module with import FS from “FS “; Exported FS module, the difference is that the FS module API uses callback, while THE FS/Promises module API supports Promise, which is more convenient for developers to use.

import fs from "fs/promises";
import { srcPath } from "./const";
export default async() = > {const dirLists = await fs.readdir(srcPath);
    return dirLists;
}
Copy the code

SrcPath is actually an absolute path, and that’s just one line of code in the conconst. Ts file.

import path from 'path';
/ /...
export const srcPath = path.join(__dirname, '.. /src');

Copy the code

The file naming function is the getName(list) method, and the list argument is the directory selected by the user.

/ /...
async function getName(fileName: string) {
    / /...
    let { input } = await inquirer.prompt([
        {
            name: 'input'.type: 'input'.message: 'Please name the file you created :',}])if (isEmpty(input)) {
        err('error: create file unnamed ');
        return
    }
    const { isSpecialChar } = specialChar(input);
    const { isCh } = ch(input);
    if (isSpecialChar) {
        err('error: File name contains special characters! ');
        return
    }

    if (isCh) {
        err('error: File name contains Chinese! ');
        return
    }
    / /...
}

Copy the code

Name verification:

  • Cannot contain special characters;
  • Chinese is not allowed.

Effect demonstration:

After the file is named, the next step is to create the corresponding file:

import path from "path";
import { testPath, srcPath, docsPath, examplePath, err } from './const';
/ /...
async function getName(fileName: string) {
    const createPath = path.join(srcPath, fileName);
    const createDocsPath = path.join(docsPath, fileName);
    const createTestPathPath = path.join(testPath, fileName);
    const createExamplePath = path.join(examplePath, fileName)
    / /...
    createTestFile(createTestPathPath, input)
    createFile(createPath, input);
    createDocs(createDocsPath, input);
    createExample(createExamplePath, input);
   / /...
}
/ /...
Copy the code

CreatePath, createTestPathPath, createDocsPath, createExamplePath Ts in the SRC file, xxx.test.ts in the test folder, xxx.md in the docs folder, and xxx.ts in the example folder.

import path from 'path';
/ /...
export const srcPath = path.join(__dirname, '.. /src');
export const testPath = path.join(__dirname, '.. /test');
export const docsPath = path.join(__dirname, '.. /docs/v3');
export const examplePath = path.join(__dirname, '.. /example');
/ /...
Copy the code

The createTestFile, createFile, createDocs, and createExample methods have similar logic except that the file name extension is created and the content is written.

Create createAndWrite.ts file to separate the common create folder from the create file and write function:

import fs from "fs";
import path from 'path';
import { err } from "./const";
export function create(createPath: string, suffixName: string) {
    !fs.existsSync(createPath) && fs.mkdirSync(createPath);
    return path.join(createPath, suffixName)
}
export function write(createPath: string, context: string, callBack = () => { }) {
    if (fs.existsSync(createPath)) {
        err(createPath + 'File already exists! ')
        return;
    }
    fs.writeFile(createPath, context, (error) = > {
        if (error) {
            err(createPath + 'File creation failed! ')
            return; } callBack(); })}Copy the code

The create method is used to create folders and the write method is used to create files and write content.

The logic of createTestFile, createFile, createDocs, createExample is similar, so I’ll just paste the createFile file:

import { srcContext } from './const';
import { write, create } from './createAndWrite';
export default (createPath: string.name: string) = > {const suffixName = name + '.ts';
    createPath = create(createPath, suffixName);
    write(createPath, srcContext())
}
Copy the code

CreateTestFile, createFile, createDocs, createExample Const suffixName = name + ‘.ts’; And then there’s srcContext.

Ts indicates the file name extension. Md,.test.ts, and.ts are required. SrcContext is a function that writes to a file. Different files write to different content. CreateTestFile, createFile, createDocs, and createExample use different function names to obtain the written content.

The content written to the file is defined in advance in the consts.ts file. The code will not be pasted here. For interested readers, please click on the source address below.

Source code address: github.com/CatsAndMice…

Overall code directory:

Source code address: github.com/CatsAndMice…

Finally, the new method automatically import main.ts and export problems are solved.

Similarly, I add an addMainContext method to the getName method in the create.ts file:

import path from "path";
/ /...
import addMainContext from './addMainContext';
/ /...

async function getName(fileName: string) {
    const createPath = path.join(srcPath, fileName);
   / /...
    createExample(createExamplePath, input);
    addMainContext(createPath, input);
}
/ /...
Copy the code

In the addMainContext file, the code logic reads the main.ts content, segments the content, determines whether it has been repeated to add the new method, and finally writes main.ts again.

Take a look at main.ts:

/ /...
import toArray from "./src/Array/toArray";
import ch from "./src/RegExp/ch";
export {
    ch,
    toArray,
    or,
    chain,
    composePromise
    / /...
 }
 export default {
    ch,
    toArray,
    or,
    chain,
    composePromise
    / /...
 }
Copy the code

There are two export methods, both of which start with the keyword export. Therefore, the contents of main.ts are read and segmented with export, and the contents are divided into three parts and stored in an array.

import fs from "fs";
/ /...
export default (path: string.name: string) => {
    filePath = path
    fs.readFile('./main.ts'.'utf-8'.(readError, data) = > {
        if (readError) {
            console.error(readError);
            return;
        }
        let context = addContext(data.split('export'), name);
        context ? writeFile(context) : null})}Copy the code

The first part is the import file, and the second and third parts are all exports. The imported content part is used to determine whether the imported content is repeated. The exported part is segmented again with {. After the segmented part is completed, the new method name is spliced again.

const getFilePath = function (filePath: string, name: string) {
    let afterPath = filePath.split('src') [1];
    afterPath = afterPath.replace('\ \'.'/');
    return './src' + afterPath + '/' + name;
}
const addContext = (args: string[], name: string) = > {
    let importsHeader = `import ${name} from "${getFilePath(filePath, name)}"; \r\n`;
    if (args[0].includes(importsHeader)) {
        err(name + 'Imported! ');
        return
    }
    let imports = args[0] + importsHeader;
    let contexts = args[1].split('{');
    let ctx = contexts[0] + `{\r\n${name}, ` + contexts[1];
    let dafaultContexts = args[2].split('{')
    let ctxs = dafaultContexts[0] + `{\r\n${name}, ` + dafaultContexts[1];
    return [imports, ctx, ctxs].join('export');
}
Copy the code

Finally, the content is written to main.ts.

const writeFile = (context: string) = > {
    fs.writeFile('./main.ts', context, (error) = > {
        console.error(error); })}export default (path: string.name: string) => {
    filePath = path
    fs.readFile('./main.ts'.'utf-8'.(readError, data) = > {
        if (readError) {
            console.error(readError);
            return;
        }
        let context = addContext(data.split('export'), name);
        context ? writeFile(context) : null})}Copy the code

Effect demonstration:

Source code address: github.com/CatsAndMice…

Automatically generate a directory list

Here I use a document site generator, Docsify, for deploying documents.

Can you help me solve any problems

  • All documents are placed ondocs/v3Under the folder, Docsify generates the document site, provided the document file path is taken to[XXX](relative path)Is included in the_sidebar.mdIt is too troublesome to write the files one by one manually, SO I want to type them automatically.

After typing, run Docsify to get a link to an online documentation site.

Open lu

In the section on automatically generating template files, I mentioned a createDocs method that creates.md files and writes to them. This is where the auto-generate directory implementation starts.

Let me show you the directory it’s in:

From the comments above, you can see that the entry point for creating a directory is the readDir function, which is passed as an argument to the write method. The purpose of this is to update the directory only after.md is created.

Get the folder directory, iterate through creating asynchronous functions to concatenate [XXX](relative paths), and add asynchronous functions to Promises array.

// Create directory
const readDir = async() = > {let content = '* [Quick Start](readme.md)\n';
    const dirs = await fsPromises.readdir(docsPath);
    const promises: any[] = [];
    dirs.forEach((dir) = > {
        const promise = new Promise(async (resolve) => {
            const filePath = path.join(docsPath, dir);
            fsPromises.readdir(filePath).then(files= > {
                content += getContent(dir, files);
                resolve(content);
            })
        })
        promises.push(promise);
    })
    // Closure to get the value of content
    allPromisesFinish(promises, () = > content);
}

Copy the code

content += getContent(dir, files); GetContent is used to sort and concatenate content formats, which are then concatenated with the variable Content.

const getContent = (dir, files) = > {
    let text = ` *${dir}\n`;
    files.sort((a, b) = > a - b);
    for (const file of files) {
        text += `  * [${file.split('. ') [0]}](v3/${dir}/${file})\n`;
    }
    return text;
}
Copy the code

Promises array will execute () => {writeContent(content()); } write the content to _sidebar. Md.

const writeContent = (content) = > {
    const writeFilePath = path.join(__dirname, '.. /docs/_sidebar.md');
    fsPromises.writeFile(writeFilePath, content);
}

// All Promise states are completed before the file is written
const allPromisesFinish = (promises, content) = > {
    Promise.all(promises).then(() = >{ writeContent(content()); })}Copy the code

A simple directory generation function is complete.

Effect demonstration:

Source code address: github.com/CatsAndMice…

The last

This article introduces the author’s use of nodeJs to solve some repetitive operations, which is also a deliberate exercise on nodeJs file modules.

Code words are not easy! If my article is helpful to you, your 👍 is my biggest support ^_^.