Like WebPack, you can modify the source code of the corresponding file using the corresponding Loarder based on the configuration file.

Micro channel small program converter (a) : the implementation of the converter.

Wechat applet converter (2) : recursive operation file.

Wechat applet converter (iii) : Loader design and implementation.

Wechat applet converter (four) : asynchronous Loader implementation.

Loader configuration in the configuration file

You can match the rule according to the configuration file to execute the corresponding Loader.

// analyze.config.js

/ / into the loader
const jsLoader = require('./lib/jsLoader')
const jsonLoader = require('./lib/jsonLoader')
const cssLoader = require('./lib/cssLoader')
const htmlLoader = require('./lib/htmlLoader')
const signLoader = require('./lib/signLoader')

const config = {
    entry: '/'.output: {
        name: 'dist'.src: '/'
    },
    module: [{test: /\.js$/,
            loader: [signLoader, jsLoader],
        },
        {
            test: /\.wxss$/,
            loader: [cssLoader],
            outputPath: (outputPath) = > outputPath.replace('.wxss'.'.acss')}, {test: /\.wxml$/,
            loader: [htmlLoader],
            outputPath: (outputPath) = > outputPath.replace('.wxml'.'.axml')}, {test: /\.json$/,
            loader: [jsonLoader],
        },
    ]
}

module.exports = config

Copy the code

Specific loader implementation

In the jsLoader example, take the source code as an argument and return the compiled new source code

// The js converter encapsulated in the previous articles
const JsParser = require('./JsParser')

function loader(source) {
    
    const jsParser = new JsParser()
    let ast = jsParser.parse(source)
    ast = jsParser.astConverter(ast)

    return jsParser.astToCode(ast)
}

module.exports = loader

Copy the code

Select loader for different files

// Rewrite the analyzeFileToLoard file analysis part of the Analyze function
function Analyze(filePath, outputPath){
    if (fs.statSync(filePath).isDirectory()) {
        const files = fs.readdirSync(filePath)
        files.forEach(file= > {
            const currentFilePath = filePath+'/'+file
            const currentOutputPath = outputPath+'/'+file
            if(fs.statSync(currentFilePath).isDirectory()) {
                fs.mkdirSync(currentOutputPath)
                Analyze(currentFilePath, currentOutputPath)
            } else analyzeFileToLoard(currentFilePath, currentOutputPath)
        })
    } else analyzeFileToLoard(filePath, outputPath)
}
Copy the code
function analyzeFileToLoard(inputPath, outputPath) {
    let source = readFile(inputPath) // Read the source code
    const loaders = config.module
    loaders.forEach(loader= > { // Traverses the configuration file to see if there is a loader rule matching the file
        if (loader.test.test(inputPath)) {
            / / use the loader
            source = useLoader(source, loader.loader, outputPath)
            // Output path handler function
            if (loader.outputPath) outputPath = loader.outputPath(outputPath)
        }
    })

    writeFile(outputAppPath(outputPath), source) // Write the processed source code to a file
}

Copy the code

Loader filters and executes

The loader executes in reverse order, running from right to left. Here we first use the synchronous loader to do the discussion.

There is still a pitch stage before loader execution, I feel pitch is not particularly appropriate, I prefer to call it filtering stage. The pitch method is executed sequentially. If the pitch method returns a value, the loader executed before the pitch method is not executed.

function useLoader(source, loaders = []) {
    // Execute the loader storage list
    const loaderList = []
	
    // Recursively filter the loader that needs to be executed
    function loaderFilter(loaders) {
        const [firstLoader, ...ortherLoader] = loaders
        if (loaders.length === 0) return
        // Execute the pitch and pass the rest of the loader as arguments
        if (firstLoader.pitch && firstLoader.pitch(ortherLoader)) return ortherLoader
        else {
            // Add the available Loader to the list
            loaderList.push(firstLoader)
            // The rest of the loader is called recursively as arguments
            loaderFilter(ortherLoader)
        }
    }
	
    // For the time being...
    const remainLoader = loaderFilter(loaders)
	
    // Synchronize the loader in reverse order
    function runLoader(source, loaderList) {
        const loader = loaderList.pop()
        let newSource = loader(source)
        if (loaderList.length > 0) return runLoader(newSource, loaderList)
        else return newSource
    }

    source = runLoader(source, loaderList)

    return source
}

Copy the code

The experiment

Write signLoader to see if loader can execute in reverse order as we want

function loader(source) {
let sign = '/** * @author: LY */'
    source = sign + source
    return source
}

module.exports = loader

Copy the code

Results:

This simple loader part is complete, but this writing can only execute some synchronous loaders, asynchronous loaders cannot wait to complete the writing.