顯示具有 JavaScript 標籤的文章。 顯示所有文章
顯示具有 JavaScript 標籤的文章。 顯示所有文章

2024/11/03

使用NX 體驗 Monorepo的美好

什麼是 Monorepo?為什麼要使用它?

隨著軟體專案的成長,特別是在多模組、多應用程式的環境下,管理代碼的挑戰也隨之增加。傳統的 Multi-Repo 策略(每個模組使用獨立儲存庫)在版本一致性和依賴管理方面存在不少痛點,特別是當需要頻繁更新和協作時,容易引發衝突和整合困難。

Monorepo 是一種更集中的管理方式。透過將所有模組放在一個儲存庫中,開發團隊可以更容易地維持一致的依賴版本和共享代碼。這種方法在大型專案中具備幾個顯著優勢:

  • 依賴一致性:Monorepo 確保所有模組使用相同版本的依賴,避免因為版本衝突而引起的問題。
  • 跨模組重用和同步:允許模組之間共享代碼,並在單一提交中完成多個模組的變更,方便同步更新。
  • 統一的 CI/CD 管理:可以使用一套測試和構建流程來處理所有模組,簡化 CI/CD 配置。
  • 增量編譯和快取:通過增量構建和快取技術(如 NX 的增量編譯),Monorepo 能顯著減少重複編譯的時間,提高開發效率。

NX:專為 Monorepo 優化的開發工具

NX 是一個強大的 Monorepo 工具,專為管理多模組、多應用的專案而設計。它支援增量編譯、自動快取、依賴視覺化等功能,幫助開發團隊減少不必要的構建,提升整體開發效率。此外,NX 還具有任務調度、代碼生成和豐富的插件生態,適合大型團隊的專案開發需求。

此外,NX 可以與高效的包管理器 pnpm 搭配使用。pnpm 具備高度優化的快取和依賴管理能力,使得構建和測試更加高效。接下來,我們將介紹如何使用 NX 和 pnpm 建立並管理一個 Monorepo 專案,並說明其主要操作指令。

2024/07/23

LeetCode 28. Find the Index of the First Occurrence in a String

/**
 * @param {string} haystack
 * @param {string} needle
 * @return {number}
 */
var strStr = function (haystack, needle) {
    return haystack.indexOf(needle)
};

2024/07/19

LeetCode 3131. Find the Integer Added to Array I

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number}
 */
var addedInteger = function (nums1, nums2) {
    if (nums1.length < 1 || nums2.length > 100 || nums1.length !== nums2.length) {
        return 0
    }

    const sum = (p, c) => p + c

    const sum1 = nums1.reduce(sum)
    const sum2 = nums2.reduce(sum)
    const total = sum2 > sum1 ? sum2 - sum1 : -(sum1 - sum2)

    return (total % nums1.length) === 0 ? total / nums1.length : 0
};

2024/07/18

LeetCode 1920. Build Array from Permutation

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var buildArray = function (nums) {
    if (nums.length < 1 || nums.length > 1000) {
        return []
    }
    const result = []
    nums.forEach((_, i) => result.push(nums[nums[i]]))

    return result
};

LeetCode 3110. Score of a String

/**
 * @param {string} s
 * @return {number}
 */
var scoreOfString = function (s) {
    if (s.length < 2 || s.length > 100) {
        return 0
    }

    let sum = 0
    let j = 1
    for (let i = 0; i < s.length - 1; i++) {
        sum += Math.abs(s[i].charCodeAt(0) - s[j++].charCodeAt(0))
    }

    return sum
};

LeetCode 2727. Is Object Empty

/**
 * @param {Object|Array} obj
 * @return {boolean}
 */
var isEmpty = function (obj) {
    if (obj instanceof Object)
        return !obj || Object.keys(obj).length === 0
    else if (obj instanceof Array)
        return !obj || obj.length === 0
};

LeetCode 2652. Sum Multiples

/**
 * @param {number} n
 * @return {number}
 */
var sumOfMultiples = function (n) {
    if (n < 1 || n > Math.pow(10, 3)) {
        return 0
    }

    let sum = 0
    const ary = Array.from({length: n}, (_, i) => i + 1)
    ary.forEach(i => {
        if (i % 3 === 0 || i % 5 === 0 || i % 7 === 0) {
            sum += i
        }
    })

    return sum
};

LeetCode 2000. Reverse Prefix of Word

/**
 * @param {string} word
 * @param {character} ch
 * @return {string}
 */
var reversePrefix = function (word, ch) {
    if (word.length < 1 || word.length > 250) {
        return ""
    }

    const index = word.indexOf(ch)

    if (index === -1) {
        return word
    } else {
        const revertStr = word.substring(0, index + 1)
        const lastStr = word.substring(index + 1)
        let newString = ""
        for (let i = revertStr.length - 1; i >= 0; i--) {
            newString += revertStr[i]
        }

        return `${newString}${lastStr}`
    }

};

LeetCode 2894. Divisible and Non-divisible Sums Difference

/**
 * @param {number} n
 * @param {number} m
 * @return {number}
 */
var differenceOfSums = function (n, m) {
    if (n < 1 || m > 1000) {
        return 0
    }

    const nums = Array.from({length: n}, (_, i) => i + 1)

    let num1 = 0
    let num2 = 0
    nums.forEach(n => {
        if (n % m !== 0) {
            num1 += n
        } else {
            num2 += n
        }
    })

    return num1 - num2
};

LeetCode 1108. Defanging an IP Address

/**
 * @param {string} address
 * @return {string}
 */
var defangIPaddr = function (address) {
    return address.replaceAll(".", "[.]")
};

LeetCode 2469. Convert the Temperature

/**
 * @param {number} celsius
 * @return {number[]}
 */
var convertTemperature = function (celsius) {
    return [
        celsius + 273.15,
        celsius * 1.80 + 32.00
    ]
};

LeetCode 1929. Concatenation of Array

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var getConcatenation = function (nums) {
    if (nums.length < 1 || nums.length > 1000) {
        return []
    }
    nums.push(...nums)
    return nums
};

LeetCode 2723. Add Two Promises

/**
 * 接收兩個Promise,當它們都解析完成後,將它們的結果相加並返回一個新的Promise。
 * 
 * @param {Promise} promise1 - 第一個Promise,應該解析為數值。
 * @param {Promise} promise2 - 第二個Promise,應該解析為數值。
 * @return {Promise} - 解析為兩個Promise結果相加的值。
 */
var addTwoPromises = async function(promise1, promise2) {
    // 等待第一個Promise解析,並將結果賦值給num1
    const num1 = await promise1;
    // 等待第二個Promise解析,並將結果賦值給num2
    const num2 = await promise2;
    // 返回一個新的Promise,解析為num1和num2的和
    return num1 + num2;
};

2620. Counter

/**
 * @param {number} n
 * @return {Function} counter
 */
var createCounter = function (n) {

    return function () {
        return n++
    };
};

LeetCode 2621. Sleep

/**
 * 暫停指定的毫秒數後返回一個 Promise
 * @param {number} millis - 要暫停的毫秒數
 * @return {Promise} - 一個 Promise,在指定毫秒數後 resolve,並返回經過的時間
 */
async function sleep(millis) {
    // 獲取當前時間戳
    const t = Date.now();
    
    // 返回一個 Promise,該 Promise 在 setTimeout 完成後 resolve
    return new Promise(resolve =>
        // 設置定時器,暫停指定毫秒數後執行回調函數
        setTimeout(() => 
            // 在回調函數中,resolve Promise,並傳入經過的時間
            resolve(Date.now() - t), 
            millis
        )
    );
}

LeetCode 258. Add Digits

/**
 * 將數字各位數字相加,直到結果為一個個位數
 * @param {number} num - 輸入的整數
 * @return {number} - 相加結果的個位數
 */
var addDigits = function (num) {
    // 檢查輸入數字是否在有效範圍內
    if (num < 0 || num > Math.pow(2, 31) - 1) {
        return 0;
    }

    // 重複相加數字的每一位,直到結果為個位數
    while (num >= 10) {
        // 將數字轉換為字符串
        const str = num.toString();
        // 將字符串分割為字符數組
        const array = str.split('');
        // 初始化相加結果
        let result = 0;
        // 遍歷字符數組,將每個字符轉換為數字並相加
        array.forEach(i => result += parseInt(i));
        // 更新 num 為相加結果
        num = result;
    }

    // 返回最終結果,即個位數
    return num;
};

LeetCode 169. Majority Element

/**
 * 找到數組中的多數元素(即出現次數超過 n/2 的元素)
 * @param {number[]} nums - 整數數組
 * @return {number} - 多數元素
 */
var majorityElement = function(nums) {
    // 檢查數組長度是否在有效範圍內
    if (nums.length < 1 || nums.length > Math.pow(10, 4) * 5) {
        return 0;
    }

    // 將數組按升序排序
    nums = nums.sort((a, b) => a - b);

    // 返回數組中間的元素,因為排序後中間的元素必定是多數元素
    return nums[Math.floor(nums.length / 2)];
};

LeetCode 71. Excel Sheet Column Number

問題:給定一個字符串 columnTitle,它表示 Excel 表中的列標題,返回其對應的列號。

例如:

A -> 1
B -> 2
C -> 3
... Z -> 26
AA -> 27
AB -> 28
...
/**
 * 將 Excel 列標題轉換為數字
 * @param {string} columnTitle - Excel 列標題
 * @return {number} - 對應的數字
 */
var titleToNumber = function (columnTitle) {
    // 檢查輸入的長度是否在 1 到 7 之間
    if (columnTitle.length < 1 || columnTitle.length > 7) {
        return 0;
    }

    // 初始化結果變量 sum 和指數變量 exp
    let sum = 0;
    let exp = 0;

    // 從字符串的最後一位開始遍歷
    for (let i = columnTitle.length - 1; i >= 0; i--) {
        // 將字符轉換為對應的數字,A 對應 1,B 對應 2,依此類推
        let charValue = columnTitle.charCodeAt(i) - 64;
        // 計算該字符對應的數字值,並加到 sum 中
        sum += charValue * Math.pow(26, exp++);
    }

    // 返回最終計算結果
    return sum;
};

LeetCode 14. Longest Common Prefix

問題:用於找到字符串數組中的最長公共前綴。如果沒有公共前綴,則返回空字符串。
/**
 * 找到字符串數組中的最長公共前綴
 * @param {string[]} strs - 字符串數組
 * @return {string} - 最長公共前綴,如果沒有公共前綴則返回空字符串
 */
var longestCommonPrefix = function (strs) {
    // 如果字符串數組為空或長度超過200,返回空字符串
    if (strs.length <= 0 || strs.length > 200) {
        return "";
    }

    // 如果只有一個字符串,返回該字符串
    if (strs.length === 1) {
        return strs[0];
    }

    // 將字符串數組按長度排序,最短的字符串在最前面
    strs = strs.sort((a, b) => a.length - b.length);
    // 取出最短的字符串作為參考
    const first = strs.pop();
    // 用於存儲公共前綴的字符數組
    const ary = [];
    
    // 遍歷參考字符串中的每個字符
    for (let i = 0; i < first.length; i++) {
        // 過濾出當前字符位置與參考字符串相同的字符串
        const filter = strs.filter(s => s[i] === first[i]);
        // 如果有任何字符串在當前字符位置不匹配,跳出循環
        if (filter.length !== strs.length) {
            break;
        }
        // 將匹配的字符添加到公共前綴字符數組中
        ary.push(first[i]);
    }

    // 如果公共前綴字符數組不為空,將其轉換為字符串並返回,否則返回空字符串
    return ary.length > 0 ? ary.join('') : "";
};


LeetCode 20. Valid Parentheses

給定一個只包含字符 '(', ')', '{', '}', '[' 和 ']' 的字符串 s,判斷輸入的字符串是否有效。
如果輸入字符串滿足以下條件則為有效:
  1. 開括號必須由相同類型的閉括號關閉。
  2. 開括號必須以正確的順序關閉。
  3. 每個閉括號都有一個相應類型的開括號。
/**
 * @param {string} s - 輸入的字符串,包含括號字符
 * @return {boolean} - 如果括號有效則返回 true,否則返回 false
 */
var isValid = function(s) {
    // 如果字符串的長度不在 1 到 10^4 之間,返回 false
    if (s.length < 1 || s.length > Math.pow(10, 4)) {
        return false;
    }

    // 建立括號匹配的映射
    const map = new Map([
        [")", "("],
        ["]", "["],
        ["}", "{"]
    ]);

    // 用於存儲開括號的堆疊
    const stackArray = [];

    // 遍歷字符串中的每個字符
    for (let i = 0; i < s.length; i++) {
        const char = s[i];

        // 如果是閉括號,檢查堆疊頂部是否匹配
        if (map.has(char)) {
            // 如果堆疊為空或堆疊頂部的括號不匹配,返回 false
            if (stackArray.length === 0 || stackArray.pop() !== map.get(char)) {
                return false;
            }
        } else {
            // 如果是開括號,壓入堆疊
            stackArray.push(char);
        }
    }

    // 如果堆疊為空,則所有括號都匹配
    return stackArray.length === 0;
};