/
githubmirror
/
webpack
Обзор
Документация
Войти
/
githubmirror
/
webpack
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v5.109.0
lib/util/magicComment.js
167 строк
5 KB
Xiao
perf: share magic comment parsing in JS and CSS parsers (#21377)
11 июл 2026, 16:10
Не верифицирован
11 июл 2026, 16:10
7e05eb1
Код
Авторство
О чём код?
/* MIT License http://www.opensource.org/licenses/mit-license.php Author Alexander Akait @alexander-akait */ "use strict"; const binarySearchBounds = require("./binarySearchBounds"); const memoize = require("./memoize"); const getVm = memoize(() => require("vm")); const CompilerHintNotationRegExp = Object.freeze({ Pure: /^\s*(?:#|@)__PURE__\s*$/, NoSideEffects: /^\s*[#@]__NO_SIDE_EFFECTS__\s*$/ }); // Whole-comment `webpackXxx: <bool|number|null>` pair — parsed without `vm`. const MAGIC_COMMENT_FAST_PATH = /^\s*(webpack[A-Z][A-Za-z]+)\s*:\s*(true|false|null|-?\d+(?:\.\d+)?)\s*$/; const webpackCommentRegExp = new RegExp(/(^|\W)webpack[A-Z][A-Za-z]+:/); /** @type {Readonly<{ options: null, errors: null }>} */ const EMPTY_COMMENT_OPTIONS = Object.freeze({ options: null, errors: null }); /** * @returns {import("vm").Context} magic comment context */ const createMagicCommentContext = () => getVm().createContext(undefined, { name: "Webpack Magic Comment Parser", codeGeneration: { strings: false, wasm: false } }); /** * Parse one magic comment's text and merge into `options`. Values are detached * from the vm context (RegExps recreated, other objects JSON-cloned). Throws * when the comment body fails to evaluate. * @param {Record<string, EXPECTED_ANY>} options target to merge into * @param {string} value comment text (should already match `webpackCommentRegExp`) * @param {import("vm").Context} context context from `createMagicCommentContext` * @returns {void} */ const assignMagicCommentOptions = (options, value, context) => { const fast = MAGIC_COMMENT_FAST_PATH.exec(value); if (fast !== null) { const raw = fast[2]; options[fast[1]] = raw === "true" ? true : raw === "false" ? false : raw === "null" ? null : Number(raw); return; } for (let [key, val] of Object.entries( getVm().runInContext(`(function(){return {${value}};})()`, context) )) { if (typeof val === "object" && val !== null) { val = val.constructor.name === "RegExp" ? new RegExp(val) : JSON.parse(JSON.stringify(val)); } options[key] = val; } }; /** * Parse one magic comment's text into its options object. * @param {string} value comment text (should already match `webpackCommentRegExp`) * @param {import("vm").Context} context context from `createMagicCommentContext` * @returns {Record<string, EXPECTED_ANY>} parsed options */ const parseMagicComment = (value, context) => { /** @type {Record<string, EXPECTED_ANY>} */ const options = {}; assignMagicCommentOptions(options, value, context); return options; }; /** * `binarySearchBounds` comparator for `getCommentsInRange`. * @param {{ range: [number, number] }} comment comment * @param {number} needle needle (byte offset) * @returns {number} comparison */ const compareCommentStart = (comment, needle) => comment.range[0] - needle; /** * Comments fully inside `range`, via binary search over source-ordered `comments`. * @template {object} TComment * @param {(TComment & { range: [number, number] })[]} comments source-ordered comments * @param {[number, number]} range range * @returns {TComment[]} comments in the range */ const getCommentsInRange = (comments, range) => { if (comments.length === 0) return []; const [start, end] = range; let idx = binarySearchBounds.ge(comments, start, compareCommentStart); /** @type {TComment[]} */ const commentsInRange = []; while (comments[idx] && comments[idx].range[1] <= end) { commentsInRange.push(comments[idx]); idx++; } return commentsInRange; }; /** * Merge webpack magic-comment options from `comments` in source order. * @template {object} TComment * @param {(TComment & { value: string })[]} comments comments fully inside the range * @param {import("vm").Context} context context from `createMagicCommentContext` * @returns {{ options: Record<string, EXPECTED_ANY> | null, errors: (Error & { comment: TComment })[] | null }} result */ const parseMagicCommentOptions = (comments, context) => { if (comments.length === 0) { return /** @type {{ options: Record<string, EXPECTED_ANY> | null, errors: (Error & { comment: TComment })[] | null }} */ ( EMPTY_COMMENT_OPTIONS ); } /** @type {Record<string, EXPECTED_ANY>} */ const options = {}; /** @type {(Error & { comment: TComment })[]} */ const errors = []; for (const comment of comments) { const { value } = comment; if (value && webpackCommentRegExp.test(value)) { try { assignMagicCommentOptions(options, value, context); } catch (err) { const newErr = new Error(String(/** @type {Error} */ (err).message)); newErr.stack = String(/** @type {Error} */ (err).stack); Object.assign(newErr, { comment }); errors.push(/** @type {Error & { comment: TComment }} */ (newErr)); } } } return { options, errors }; }; /** * Merge webpack magic-comment options from comments inside `range`. * @template {object} TComment * @param {(TComment & { range: [number, number], value: string })[]} comments source-ordered comments * @param {[number, number]} range range * @param {import("vm").Context} context context from `createMagicCommentContext` * @returns {{ options: Record<string, EXPECTED_ANY> | null, errors: (Error & { comment: TComment })[] | null }} result */ const parseCommentOptionsInRange = (comments, range, context) => parseMagicCommentOptions(getCommentsInRange(comments, range), context); module.exports = { CompilerHintNotationRegExp, createMagicCommentContext, getCommentsInRange, parseCommentOptionsInRange, parseMagicComment, webpackCommentRegExp };