市场夺宝奇兵
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

270 lines
6.9 KiB

  1. //#region src/utils.ts
  2. const postfixRE = /[?#].*$/;
  3. function cleanUrl(url) {
  4. return url.replace(postfixRE, "");
  5. }
  6. function extractQueryWithoutFragment(url) {
  7. const questionMarkIndex = url.indexOf("?");
  8. if (questionMarkIndex === -1) return "";
  9. const fragmentIndex = url.indexOf("#", questionMarkIndex);
  10. if (fragmentIndex === -1) return url.substring(questionMarkIndex);
  11. else return url.substring(questionMarkIndex, fragmentIndex);
  12. }
  13. //#endregion
  14. //#region src/composable-filters.ts
  15. var And = class {
  16. kind;
  17. args;
  18. constructor(...args) {
  19. if (args.length === 0) throw new Error("`And` expects at least one operand");
  20. this.args = args;
  21. this.kind = "and";
  22. }
  23. };
  24. var Or = class {
  25. kind;
  26. args;
  27. constructor(...args) {
  28. if (args.length === 0) throw new Error("`Or` expects at least one operand");
  29. this.args = args;
  30. this.kind = "or";
  31. }
  32. };
  33. var Not = class {
  34. kind;
  35. expr;
  36. constructor(expr) {
  37. this.expr = expr;
  38. this.kind = "not";
  39. }
  40. };
  41. var Id = class {
  42. kind;
  43. pattern;
  44. params;
  45. constructor(pattern, params) {
  46. this.pattern = pattern;
  47. this.kind = "id";
  48. this.params = params ?? { cleanUrl: false };
  49. }
  50. };
  51. var ModuleType = class {
  52. kind;
  53. pattern;
  54. constructor(pattern) {
  55. this.pattern = pattern;
  56. this.kind = "moduleType";
  57. }
  58. };
  59. var Code = class {
  60. kind;
  61. pattern;
  62. constructor(expr) {
  63. this.pattern = expr;
  64. this.kind = "code";
  65. }
  66. };
  67. var Query = class {
  68. kind;
  69. key;
  70. pattern;
  71. constructor(key, pattern) {
  72. this.pattern = pattern;
  73. this.key = key;
  74. this.kind = "query";
  75. }
  76. };
  77. var Include = class {
  78. kind;
  79. expr;
  80. constructor(expr) {
  81. this.expr = expr;
  82. this.kind = "include";
  83. }
  84. };
  85. var Exclude = class {
  86. kind;
  87. expr;
  88. constructor(expr) {
  89. this.expr = expr;
  90. this.kind = "exclude";
  91. }
  92. };
  93. function and(...args) {
  94. return new And(...args);
  95. }
  96. function or(...args) {
  97. return new Or(...args);
  98. }
  99. function not(expr) {
  100. return new Not(expr);
  101. }
  102. function id(pattern, params) {
  103. return new Id(pattern, params);
  104. }
  105. function moduleType(pattern) {
  106. return new ModuleType(pattern);
  107. }
  108. function code(pattern) {
  109. return new Code(pattern);
  110. }
  111. function query(key, pattern) {
  112. return new Query(key, pattern);
  113. }
  114. function include(expr) {
  115. return new Include(expr);
  116. }
  117. function exclude(expr) {
  118. return new Exclude(expr);
  119. }
  120. /**
  121. * convert a queryObject to FilterExpression like
  122. * ```js
  123. * and(query(k1, v1), query(k2, v2))
  124. * ```
  125. * @param queryFilterObject The query filter object needs to be matched.
  126. * @returns a `And` FilterExpression
  127. */
  128. function queries(queryFilter) {
  129. let arr = Object.entries(queryFilter).map(([key, value]) => {
  130. return new Query(key, value);
  131. });
  132. return and(...arr);
  133. }
  134. function interpreter(exprs, code$1, id$1, moduleType$1) {
  135. let arr = [];
  136. if (Array.isArray(exprs)) arr = exprs;
  137. else arr = [exprs];
  138. return interpreterImpl(arr, code$1, id$1, moduleType$1);
  139. }
  140. function interpreterImpl(expr, code$1, id$1, moduleType$1, ctx = {}) {
  141. let hasInclude = false;
  142. for (const e of expr) switch (e.kind) {
  143. case "include": {
  144. hasInclude = true;
  145. if (exprInterpreter(e.expr, code$1, id$1, moduleType$1, ctx)) return true;
  146. break;
  147. }
  148. case "exclude": {
  149. if (exprInterpreter(e.expr, code$1, id$1, moduleType$1)) return false;
  150. break;
  151. }
  152. }
  153. return !hasInclude;
  154. }
  155. function exprInterpreter(expr, code$1, id$1, moduleType$1, ctx = {}) {
  156. switch (expr.kind) {
  157. case "and": return expr.args.every((e) => exprInterpreter(e, code$1, id$1, moduleType$1, ctx));
  158. case "or": return expr.args.some((e) => exprInterpreter(e, code$1, id$1, moduleType$1, ctx));
  159. case "not": return !exprInterpreter(expr.expr, code$1, id$1, moduleType$1, ctx);
  160. case "id": {
  161. if (id$1 === void 0) throw new Error("`id` is required for `id` expression");
  162. if (expr.params.cleanUrl) id$1 = cleanUrl(id$1);
  163. return typeof expr.pattern === "string" ? id$1 === expr.pattern : expr.pattern.test(id$1);
  164. }
  165. case "moduleType": {
  166. if (moduleType$1 === void 0) throw new Error("`moduleType` is required for `moduleType` expression");
  167. return moduleType$1 === expr.pattern;
  168. }
  169. case "code": {
  170. if (code$1 === void 0) throw new Error("`code` is required for `code` expression");
  171. return typeof expr.pattern === "string" ? code$1.includes(expr.pattern) : expr.pattern.test(code$1);
  172. }
  173. case "query": {
  174. if (id$1 === void 0) throw new Error("`id` is required for `Query` expression");
  175. if (!ctx.urlSearchParamsCache) {
  176. let queryString = extractQueryWithoutFragment(id$1);
  177. ctx.urlSearchParamsCache = new URLSearchParams(queryString);
  178. }
  179. let urlParams = ctx.urlSearchParamsCache;
  180. if (typeof expr.pattern === "boolean") if (expr.pattern) return urlParams.has(expr.key);
  181. else return !urlParams.has(expr.key);
  182. else if (typeof expr.pattern === "string") return urlParams.get(expr.key) === expr.pattern;
  183. else return expr.pattern.test(urlParams.get(expr.key) ?? "");
  184. }
  185. default: throw new Error(`Expression ${JSON.stringify(expr)} is not expected.`);
  186. }
  187. }
  188. //#endregion
  189. //#region src/simple-filters.ts
  190. /**
  191. * Constructs a RegExp that matches the exact string specified.
  192. *
  193. * This is useful for plugin hook filters.
  194. *
  195. * @param str the string to match.
  196. * @param flags flags for the RegExp.
  197. *
  198. * @example
  199. * ```ts
  200. * import { exactRegex } from '@rolldown/pluginutils';
  201. * const plugin = {
  202. * name: 'plugin',
  203. * resolveId: {
  204. * filter: { id: exactRegex('foo') },
  205. * handler(id) {} // will only be called for `foo`
  206. * }
  207. * }
  208. * ```
  209. */
  210. function exactRegex(str, flags) {
  211. return new RegExp(`^${escapeRegex(str)}$`, flags);
  212. }
  213. /**
  214. * Constructs a RegExp that matches a value that has the specified prefix.
  215. *
  216. * This is useful for plugin hook filters.
  217. *
  218. * @param str the string to match.
  219. * @param flags flags for the RegExp.
  220. *
  221. * @example
  222. * ```ts
  223. * import { prefixRegex } from '@rolldown/pluginutils';
  224. * const plugin = {
  225. * name: 'plugin',
  226. * resolveId: {
  227. * filter: { id: prefixRegex('foo') },
  228. * handler(id) {} // will only be called for IDs starting with `foo`
  229. * }
  230. * }
  231. * ```
  232. */
  233. function prefixRegex(str, flags) {
  234. return new RegExp(`^${escapeRegex(str)}`, flags);
  235. }
  236. const escapeRegexRE = /[-/\\^$*+?.()|[\]{}]/g;
  237. function escapeRegex(str) {
  238. return str.replace(escapeRegexRE, "\\$&");
  239. }
  240. function makeIdFiltersToMatchWithQuery(input) {
  241. if (!Array.isArray(input)) return makeIdFilterToMatchWithQuery(input);
  242. return input.map((i) => makeIdFilterToMatchWithQuery(i));
  243. }
  244. function makeIdFilterToMatchWithQuery(input) {
  245. if (typeof input === "string") return `${input}{?*,}`;
  246. return makeRegexIdFilterToMatchWithQuery(input);
  247. }
  248. function makeRegexIdFilterToMatchWithQuery(input) {
  249. return new RegExp(input.source.replace(/(?<!\\)\$/g, "(?:\\?.*)?$"), input.flags);
  250. }
  251. //#endregion
  252. exports.and = and;
  253. exports.code = code;
  254. exports.exactRegex = exactRegex;
  255. exports.exclude = exclude;
  256. exports.exprInterpreter = exprInterpreter;
  257. exports.id = id;
  258. exports.include = include;
  259. exports.interpreter = interpreter;
  260. exports.interpreterImpl = interpreterImpl;
  261. exports.makeIdFiltersToMatchWithQuery = makeIdFiltersToMatchWithQuery;
  262. exports.moduleType = moduleType;
  263. exports.not = not;
  264. exports.or = or;
  265. exports.prefixRegex = prefixRegex;
  266. exports.queries = queries;
  267. exports.query = query;