91aaa在线国内观看,亚洲AV午夜福利精品一区二区,久久偷拍人视频,久久播这里有免费视播

<strong id="fvuar"></strong>

  • <sub id="fvuar"><dl id="fvuar"><em id="fvuar"></em></dl></sub>

    1. 千鋒教育-做有情懷、有良心、有品質(zhì)的職業(yè)教育機(jī)構(gòu)

      手機(jī)站
      千鋒教育

      千鋒學(xué)習(xí)站 | 隨時隨地免費(fèi)學(xué)

      千鋒教育

      掃一掃進(jìn)入千鋒手機(jī)站

      領(lǐng)取全套視頻
      千鋒教育

      關(guān)注千鋒學(xué)習(xí)站小程序
      隨時隨地免費(fèi)學(xué)習(xí)課程

      當(dāng)前位置:首頁  >  技術(shù)干貨  > 如何用typescript寫一個處理console的babel插件

      如何用typescript寫一個處理console的babel插件

      來源:千鋒教育
      發(fā)布人:qyf
      時間: 2022-10-10 16:42:54 1665391374

        技術(shù)點介紹

        通過這篇文章你可以學(xué)到:

        · ts-mocha和chai來寫測試用例,

        · 如何寫一個babel插件,

        · 如何用schame-utils來做options校驗,

        · typescript雙重斷言的一個應(yīng)用場景

        · 如何組織測試代碼

        一、前言

        console對象對前端工程師來說是必不可少的api,開發(fā)時我們經(jīng)常通過它來打印一些信息來調(diào)試。但生產(chǎn)環(huán)境下console有時會引起一些問題。

        如果項目報了一個bug,console對象被重寫了但是沒有把所有的方法都重寫,導(dǎo)致了報錯,另外考慮到console會影響性能,所以最后定的解決方案是把源碼中所有的console都刪掉。

        生產(chǎn)環(huán)境下刪除console是沒問題的,但是這件事不需要手動去做。在打包過程中,我們會對代碼進(jìn)行壓縮,而壓縮的工具都提供了刪除一些函數(shù)的功能,比如terser支持drop_console來刪除console.*,也可以通過pure_funcs來刪除某幾種console的方法。

      圖片26

        但是這種方案對我們是不適用的,因為我們既有react的項目又有react-native的項目,react-native并不會用webpack打包,也就不會用terser來壓縮。

        其實源碼到最終代碼過程中會經(jīng)歷很多次ast的解析,比如eslint、babel、terser等,除了eslint主要是用來檢查ast,并不會做過多修改,其余的工具都可以來完成修改ast,刪除console這件事情。terser不可以用,那么我們可以考慮用babel來做。

        而且,我們只是希望在生產(chǎn)環(huán)境下刪除console,在開發(fā)環(huán)境下console還是很有用的,如果能擴(kuò)展一下console,讓它功能更強(qiáng)大,比如支持顏色的打印,支持文件和代碼行數(shù)的提示就好了。于是我們就開發(fā)了本文介紹的這個插件: babel-plugin-console-transform。

        二、演示

        先看下效果再講實現(xiàn)。比如源碼是這樣的:

      圖片27

        生產(chǎn)環(huán)境下轉(zhuǎn)換后的代碼:

      圖片28

        開發(fā)環(huán)境下轉(zhuǎn)換后的代碼:

      圖片29

        運(yùn)行效果:

      圖片30

        生產(chǎn)環(huán)境刪除了console,開發(fā)環(huán)境擴(kuò)展了一些方法,并且添加了代碼行數(shù)和顏色等。

        接下來是功能的細(xì)節(jié)還有實現(xiàn)思路。

        三、功能

        按照需求,這個插件需要在不同的環(huán)境做不同的處理,生產(chǎn)環(huán)境可以刪除console,開發(fā)環(huán)境擴(kuò)展console。

        生產(chǎn)環(huán)境刪除console并不是全部刪除,還需要支持刪除指定name的方法,比如log、warn等,因為有時console.error是有用的。而且有的時候根據(jù)方法名還不能確定能不能刪除,要根據(jù)打印的內(nèi)容來確定是不是要刪。

        開發(fā)環(huán)境擴(kuò)展console要求不改變原生的api,擴(kuò)展一些方法,這些方法會被轉(zhuǎn)換成原生api,但是會額外添加一些信息,比如添加代碼文件和行數(shù)的信息,添加一些顏色的樣式信息等。

      圖片31

        于是console-transform這個插件就有了這樣的參數(shù):

        {

        env: 'production',

        removeMethods: ["log", "*g*", (args) => args.includes('xxxx')],

        additionalStyleMethods: {

        'success': 'padding:10px; color:#fff;background:green;',

        'danger': 'padding:20px; background:red;font-size:30px; color:#fff;'

        }

        }

        其中env是指定環(huán)境的,可以通過process.env.NODE_ENV來設(shè)置。

        removeMethods是在生產(chǎn)環(huán)境下要刪除的方法,可以傳一個name,支持glob,也就是 \*g*是刪除所有名字包含g的方法;而且可以傳一個函數(shù),函數(shù)的參數(shù)是console.xxx的所有參數(shù),插件會根據(jù)這個函數(shù)的返回值來決定是不是刪除改console.xxx。多個條件的時候,只要有一個生效,就會刪。

        additionalStyleMethods里面可以寫一些擴(kuò)展的方法,比如succes、danger,分別定義了他們的樣式。其實插件本身提供了 red、green、orange、blue、bgRed、bgOrange、bgGreen、bgBlue方法,通過這個參數(shù)可以自定義,開發(fā)環(huán)境console怎么用都行。

        四、實現(xiàn)

        接下來是重頭戲,實現(xiàn)思路了。

        首先介紹下用到的技術(shù),代碼是用typescript寫的,實現(xiàn)功能是基于 @babel/core,@babel/types,測試代碼使用ts-mocha、chai寫的,代碼的lint用的eslint、prettier。

        主要邏輯

        babel會把代碼轉(zhuǎn)成ast,插件里可以對對ast做修改,然后輸出的代碼就是轉(zhuǎn)換后的。babel的插件需要是一個返回插件信息的函數(shù)。

        如下, 參數(shù)是babelCore的api,里面有很多工具,我們這里只用到了 types來生成一些ast的節(jié)點。返回值是一個PluginObj類型的對象。

        import BabelCore, { PluginObj } from '@babel/core';

        export default function({

        types,

        }: typeof BabelCore): PluginObj{

        return {

        name: 'console-transform',

        visitor: {...}

        }

        }

        其中ConsoleTransformState里面是我們要指定的類型,這是在后面對ast處理時需要拿到參數(shù)和文件信息時用的。

        export interface PluginOptions {

        env: string;

        removeMethods?: Array;

        additionalStyleMethods?: { [key: string]: string };

        }

        export interface ConsoleTransformState {

        opts: PluginOptions;

        file: any;

        }

        PluginOptions是options的類型,env是必須,其余兩個可選,removeMethods是一個值為string或Function的數(shù)組,additionalStyleMethods是一個值為string的對象。 這都是我們討論需求時確定的。(其中file是獲取代碼行列數(shù)用的,我們找到它的類型,就用了any。)

        返回的插件信息對象有一個visitor屬性,可以聲明對一些節(jié)點的處理方式,我們需要處理的是CallExpression節(jié)點。(關(guān)于代碼對應(yīng)的ast是什么樣的,可以用astexplorer這個工具看)。

        {

        CallExpression(path, { opts, file }) {

        validateSchema(schema, opts);

        const { env, removeMethods, additionalStyleMethods } = opts;

        const callee = path.get('callee');

        if (

        callee.node.type === 'MemberExpression' &&

        (callee.node.object as any).name === 'console'

        ) {

        ...

        }

        },

        }

        這個方法就會在處理到CallExpression類型的節(jié)點時被調(diào)用,參數(shù)path 可以拿到一些節(jié)點的信息,通過path.get('callee')拿到調(diào)用信息,然后通過node.type過濾 console.xxx() 而不是xxx()類型的函數(shù)調(diào)用,也就是MemberExpression類型,再通過callee.node.object過濾出console的方法。

        實現(xiàn)production下刪除console

        接下來就是實現(xiàn)主要功能的時候了:

        const methodName = callee.node.property.name as string;

        if (env === 'production') {

        ...

        return path.remove();

        } else {

        const lineNum = path.node.loc.start.line;

        const columnNum = path.node.loc.start.column;

        ...

        path.node.arguments.unshift(...);

        callee.node.property.name = 'log';

        }

        先看主要邏輯,production環(huán)境下,調(diào)用path.remove(),這樣console就沒了,其他環(huán)境對console的參數(shù)(path.node.arguments.)做一些修改,在前面多加一些參數(shù),然后把方法名(callee.node.property.name)改為log。大體框架就是這樣的。

        然后細(xì)化一下:

        production的時候,當(dāng)有removeMethods參數(shù)時,要根據(jù)其中的name和funciton來決定是否刪除:

        if (removeMethods) {

        const args = path.node.arguments.map(

        item => (item as any).value,

        );

        if (isMatch(removeMethods, methodName, args)) {

        return path.remove();

        }

        return;

        }

        return path.remove();

        通過把path.node.arguments把所有的args放到一個數(shù)組里,然后來匹配條件。如下,匹配時根據(jù)類型是string還是function決定如何調(diào)用。

        const isMatch = (

        removeMethods: Array,

        methodName: string,

        args: any[],

        ): boolean => {

        let isRemove = false;

        for (let i = 0; i < removeMethods.length; i++) {

        if (typeof removeMethods[i] === 'function') {

        isRemove = (removeMethods[i] as Function)(args) ? true : isRemove;

        } else if (mm([methodName], removeMethods[i] as string).length > 0) {

        isRemove = true;

        }

        }

        return isRemove;

        };

        如果是function就把參數(shù)作為參數(shù)傳入,根據(jù)返回值確定是否刪除,如果是字符串,會用mimimatch做glob的解析,支持**、 {a,b}等語法。

        實現(xiàn)非production下擴(kuò)展console

        當(dāng)在非production環(huán)境下,插件會提供一些內(nèi)置方法:

        const styles: { [key: string]: string } = {

        red: 'color:red;',

        blue: 'color:blue;',

        green: 'color:green',

        orange: 'color:orange',

        bgRed: 'padding: 4px; background:red;',

        bgBlue: 'padding: 4px; background:blue;',

        bgGreen: 'padding: 4px; background: green',

        bgOrange: 'padding: 4px; background: orange',

        };

        結(jié)合用戶通過addtionalStyleMethods擴(kuò)展的方法,來對代碼做轉(zhuǎn)換:

        const methodName = callee.node.property.name as string;

        const lineNum = path.node.loc.start.line;

        const columnNum = path.node.loc.start.column;

        const allStyleMethods = {

        ...styles,

        ...additionalStyleMethods,

        };

        if (Object.keys(allStyleMethods).includes(methodName)) {

        const ss = path.node.arguments.map(() => '%s').join('');

        path.node.arguments.unshift(

        types.stringLiteral(`%c${ss}%s`),

        types.stringLiteral(allStyleMethods[methodName]),

        types.stringLiteral(

        `${file.opts.filename.slice(

        process.cwd().length,

        )} (${lineNum}:${columnNum}):`,

        ),

        );

        callee.node.property.name = 'log';

        }

        通過methodName判斷出要擴(kuò)展的方法,然后在參數(shù)(path.node.arguments)中填入一些額外的信息 ,這里就用到了@babel/core提供的types(其實是封裝了@babel/types的api)來生成文本節(jié)點了,最后把擴(kuò)展的方法名都改成log。

        實現(xiàn)options的校驗

        我們邏輯寫完了,但是options還沒有校驗,這里可以用schema-utils這個工具來校驗,通過一個json對象來描述解構(gòu),然后調(diào)用validate的api來校驗。webpack那么復(fù)雜的options就是通過這個工具校驗的。

        schema如下,對env、removeMethods、additionalStyleMethods都是什么格式做了聲明。

        export default {

        type: 'object',

        additionalProperties: false,

        properties: {

        env: {

        description:

        'set the environment to decide how to handle `console.xxx()` code',

        type: 'string',

        },

        removeMethods: {

        description:

        'set what method to remove in production environment, default to all',

        type: 'array',

        items: {

        description:

        'method name or function to decide whether remove the code',

        oneOf: [

        {

        type: 'string',

        },

        {

        instanceof: 'Function',

        },

        ],

        },

        },

        additionalStyleMethods: {

        description:

        'some method to extend the console object which can be invoked by console.xxx() in non-production environment',

        type: 'object',

        additionalProperties: true,

        },

        },

        required: ['env'],

        };

        五、測試

        代碼寫完了,就到了測試環(huán)節(jié),測試的完善度直接決定了你這個工具可不可用。

      圖片32

        options的測試就是傳入各種情況的options參數(shù),看報錯信息是否正確。這里有個知識點,因為options需要傳錯,所以肯定類型不符合,使用as any as PluginOptions的雙重斷言可以繞過類型校驗。

        describe('options格式測試', () => {

        const inputFilePath = path.resolve(

        __dirname,

        './fixtures/production/drop-all-console/actual.js',

        );

        it('env缺失會報錯', () => {

        const pluginOptions = {};

        assertFileTransformThrows(

        inputFilePath,

        pluginOptions as PluginOptions,

        new RegExp(".*configuration misses the property 'env'*"),

        );

        });

        it('env只能傳字符串', () => {

        const pluginOptions = {

        env: 1,

        };

        assertFileTransformThrows(

        inputFilePath,

        (pluginOptions as any) as PluginOptions,

        new RegExp('.*configuration.env should be a string.*'),

        );

        });

        it('removeMethods的元素只能是string或者function', () => {

        const pluginOptions = {

        env: 'production',

        removeMethods: [1],

        };

        assertFileTransformThrows(

        inputFilePath,

        (pluginOptions as any) as PluginOptions,

        new RegExp(

        '.*configuration.removeMethods[.*] should be one of these:s[ ]{3}string | function.*',

        ),

        );

        });

        it('additionalStyleMethods只能是對象', () => {

        const pluginOptions: any = {

        env: 'production',

        additionalStyleMethods: [],

        };

        assertFileTransformThrows(

        inputFilePath,

        pluginOptions as PluginOptions,

        new RegExp(

        '.*configuration.additionalStyleMethods should be an object.*',

        ),

        );

        });

        });

        主要的還是plugin邏輯的測試。

        @babel/core 提供了transformFileSync的api,可以對文件做處理,我封裝了一個工具函數(shù),對輸入文件做處理,把結(jié)果的內(nèi)容和另一個輸出文件做對比。

        const assertFileTransformResultEqual = (

        inputFilePathRelativeToFixturesDir: string,

        outputFilePath: string,

        pluginOptions: PluginOptions,

        ): void => {

        const actualFilePath = path.resolve(__dirname, './fixtures/', inputFilePathRelativeToFixturesDir,);

        const expectedFilePath = path.resolve(__dirname,'./fixtures/',outputFilePath);

        const res = transformFileSync(inputFilePath, {

        babelrc: false,

        configFile: false,

        plugins: [[consoleTransformPlugin, pluginOptions]]

        });

        assert.equal(

        res.code,

        fs.readFileSync(expectedFilePath, {

        encoding: 'utf-8',

        }),

        );

        };

        fixtures下按照production和其他環(huán)境的不同場景分別寫了輸入文件actual和輸出文件expected。比如production下測試drop-all-console、drop-console-by-function等case,和下面的測試代碼一一對應(yīng)。

      圖片33

        代碼里面是對各種情況的測試。

        describe('plugin邏輯測試', () => {

        describe('production環(huán)境', () => {

        it('默認(rèn)會刪除所有的console', () => {

        const pluginOptions: PluginOptions = {

        env: 'production',

        };

        assertFileTransformResultEqual(

        'production/drop-all-console/actual.js',

        'production/drop-all-console/expected.js',

        pluginOptions,

        );

        });

        it('可以通過name刪除指定console,支持glob', () => {...});

        it('可以通過function刪除指定參數(shù)的console', () => {...}

        });

        describe('其他環(huán)境', () => {

        it('非擴(kuò)展方法不做處理', () => {...});

        it('默認(rèn)擴(kuò)展了red 、green、blue、orange、 bgRed、bgGreen等方法,并且添加了行列數(shù)', () => {...});

        it('可以通過additionalStyleMethods擴(kuò)展方法,并且也會添加行列數(shù)', () => {...});

        it('可以覆蓋原生的log等方法', () => {...});

        });

        });

        六、總結(jié)

        這個插件雖然功能只是處理console,但細(xì)節(jié)還是蠻多的,比如刪除的時候要根據(jù)name和function確定是否刪除,name支持glob,非production環(huán)境要支持用戶自定義擴(kuò)展等等。

        技術(shù)方面,用了schema-utils做options校驗,用ts-mocha結(jié)合斷言庫chai做測試,同時設(shè)計了一個比較清晰的目錄結(jié)構(gòu)來組織測試代碼。

      tags:
      聲明:本站稿件版權(quán)均屬千鋒教育所有,未經(jīng)許可不得擅自轉(zhuǎn)載。
      10年以上業(yè)內(nèi)強(qiáng)師集結(jié),手把手帶你蛻變精英
      請您保持通訊暢通,專屬學(xué)習(xí)老師24小時內(nèi)將與您1V1溝通
      免費(fèi)領(lǐng)取
      今日已有369人領(lǐng)取成功
      劉同學(xué) 138****2860 剛剛成功領(lǐng)取
      王同學(xué) 131****2015 剛剛成功領(lǐng)取
      張同學(xué) 133****4652 剛剛成功領(lǐng)取
      李同學(xué) 135****8607 剛剛成功領(lǐng)取
      楊同學(xué) 132****5667 剛剛成功領(lǐng)取
      岳同學(xué) 134****6652 剛剛成功領(lǐng)取
      梁同學(xué) 157****2950 剛剛成功領(lǐng)取
      劉同學(xué) 189****1015 剛剛成功領(lǐng)取
      張同學(xué) 155****4678 剛剛成功領(lǐng)取
      鄒同學(xué) 139****2907 剛剛成功領(lǐng)取
      董同學(xué) 138****2867 剛剛成功領(lǐng)取
      周同學(xué) 136****3602 剛剛成功領(lǐng)取
      相關(guān)推薦HOT
      大數(shù)據(jù)測試工程師需要具備哪些技能?

      一、理解大數(shù)據(jù)概念大數(shù)據(jù)測試工程師需要理解大數(shù)據(jù)的基本概念和原理,如分布式存儲、MapReduce、實時計算等。他們還需要了解如何處理大規(guī)模的...詳情>>

      2023-10-14 23:43:03
      為什么SpringBoot的 jar 可以直接運(yùn)行?

      一、JAR文件的結(jié)構(gòu)與執(zhí)行方式Spring Boot的JAR包是Java Archive的縮寫,它是一種壓縮文件格式,可以將Java項目的類文件、資源文件以及依賴庫等...詳情>>

      2023-10-14 23:01:49
      站群服務(wù)器是什么?

      站群服務(wù)器的含義與用途站群服務(wù)器主要用于支持站群,即由一組相互鏈接的網(wǎng)站組成的群體。這些網(wǎng)站通常由同一組織或個人擁有,并且經(jīng)常會互相鏈...詳情>>

      2023-10-14 22:46:12
      自編碼器是什么?

      一、自編碼器原理自編碼器的設(shè)計靈感源于神經(jīng)科學(xué)中關(guān)于感知系統(tǒng)的認(rèn)知原理,它的核心思想是將輸入數(shù)據(jù)經(jīng)過編碼過程,形成一個隱藏層的特征表示...詳情>>

      2023-10-14 22:41:10
      什么是云網(wǎng)融合?

      一、云網(wǎng)融合的定義云網(wǎng)融合是指將云計算與網(wǎng)絡(luò)技術(shù)相結(jié)合,實現(xiàn)資源的共享、業(yè)務(wù)的協(xié)同,將網(wǎng)絡(luò)與云端服務(wù)深度融合,提供更靈活、高效、安全的...詳情>>

      2023-10-14 22:31:47