feat: init

This commit is contained in:
2026-02-13 22:02:30 +01:00
commit 8f9ff830fb
16711 changed files with 3307340 additions and 0 deletions

21
node_modules/@vue-macros/common/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022-PRESENT Kevin Deng
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

124
node_modules/@vue-macros/common/dist/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,124 @@
import { CodeTransform, MagicString, MagicStringAST } from "magic-string-ast";
import { FilterPattern, FilterPattern as FilterPattern$1, createFilter as createRollupFilter, normalizePath } from "unplugin-utils";
import { SFCDescriptor, SFCParseResult, SFCScriptBlock as SFCScriptBlock$1 } from "@vue/compiler-sfc";
import * as t from "@babel/types";
import { Node, Program } from "@babel/types";
import { ResolvedOptions } from "@vitejs/plugin-vue";
import { Plugin } from "rollup";
import { HmrContext, Plugin as Plugin$1 } from "vite";
export * from "magic-string-ast";
export * from "ast-kit";
//#region src/ast.d.ts
declare function checkInvalidScopeReference(node: t.Node | undefined, method: string, setupBindings: string[]): void;
declare function isStaticExpression(node: t.Node, options?: Partial<Record<"object" | "fn" | "objectMethod" | "array" | "unary" | "regex", boolean> & {
magicComment?: string;
}>): boolean;
declare function isStaticObjectKey(node: t.ObjectExpression): boolean;
/**
* @param node must be a static expression, SpreadElement is not supported
*/
declare function resolveObjectExpression(node: t.ObjectExpression): Record<string | number, t.ObjectMethod | t.ObjectProperty> | undefined;
declare const HELPER_PREFIX = "__MACROS_";
declare function importHelperFn(s: MagicString, offset: number, imported: string, local?: string, from?: string): string;
//#endregion
//#region src/constants.d.ts
declare const DEFINE_PROPS = "defineProps";
declare const DEFINE_PROPS_DOLLAR = "$defineProps";
declare const DEFINE_PROPS_REFS = "definePropsRefs";
declare const DEFINE_EMITS = "defineEmits";
declare const WITH_DEFAULTS = "withDefaults";
declare const DEFINE_OPTIONS = "defineOptions";
declare const DEFINE_MODELS = "defineModels";
declare const DEFINE_MODELS_DOLLAR = "$defineModels";
declare const DEFINE_SETUP_COMPONENT = "defineSetupComponent";
declare const DEFINE_RENDER = "defineRender";
declare const DEFINE_SLOTS = "defineSlots";
declare const DEFINE_STYLEX = "defineStyleX";
declare const DEFINE_PROP = "defineProp";
declare const DEFINE_PROP_DOLLAR = "$defineProp";
declare const DEFINE_EMIT = "defineEmit";
declare const REPO_ISSUE_URL: "https://github.com/vue-macros/vue-macros/issues";
declare const REGEX_SRC_FILE: RegExp;
declare const REGEX_SETUP_SFC: RegExp;
declare const REGEX_SETUP_SFC_SUB: RegExp;
declare const REGEX_VUE_SFC: RegExp;
/** webpack only */
declare const REGEX_VUE_SUB: RegExp;
/** webpack only */
declare const REGEX_VUE_SUB_SETUP: RegExp;
declare const REGEX_NODE_MODULES: RegExp;
declare const REGEX_SUPPORTED_EXT: RegExp;
declare const VIRTUAL_ID_PREFIX = "/vue-macros";
//#endregion
//#region src/dep.d.ts
declare function detectVueVersion(root?: string, defaultVersion?: number): number;
//#endregion
//#region src/error.d.ts
declare class TransformError<T extends string> extends SyntaxError {
message: T;
constructor(message: T);
}
//#endregion
//#region src/unplugin.d.ts
interface FilterOptions {
include?: FilterPattern$1;
exclude?: FilterPattern$1;
}
declare function createFilter(options: FilterOptions): (id: string | unknown) => boolean;
interface VuePluginApi {
options: ResolvedOptions;
version: string;
}
declare function getVuePluginApi(plugins: Readonly<(Plugin | Plugin$1)[]> | undefined): VuePluginApi | null;
declare enum FilterFileType {
/** Vue SFC */
VUE_SFC = 0,
/** Vue SFC with `<script setup>` */
VUE_SFC_WITH_SETUP = 1,
/** foo.setup.tsx */
SETUP_SFC = 2,
/** Source files */
SRC_FILE = 3,
}
declare function getFilterPattern(types: FilterFileType[], framework?: string): RegExp[];
declare function hackViteHMR(ctx: HmrContext, filter: (id: string | unknown) => boolean, callback: (code: string, id: string) => CodeTransform | undefined | Promise<CodeTransform | undefined>): void;
//#endregion
//#region src/options.d.ts
interface BaseOptions extends FilterOptions {
version?: number;
isProduction?: boolean;
}
//#endregion
//#region src/types.d.ts
type MarkRequired<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;
type Overwrite<T, U> = Pick<T, Exclude<keyof T, keyof U>> & U;
type RecordToUnion<T extends Record<string, any>> = T[keyof T];
type UnionToIntersection<U> = (U extends unknown ? (arg: U) => 0 : never) extends ((arg: infer I) => 0) ? I : never;
//#endregion
//#region src/vue.d.ts
type SFCScriptBlock = Omit<SFCScriptBlock$1, "scriptAst" | "scriptSetupAst">;
type SFC = Omit<SFCDescriptor, "script" | "scriptSetup"> & {
sfc: SFCParseResult;
script?: SFCScriptBlock | null;
scriptSetup?: SFCScriptBlock | null;
lang: string | undefined;
getScriptAst: () => Program | undefined;
getSetupAst: () => Program | undefined;
offset: number;
} & Pick<SFCParseResult, "errors">;
declare function parseSFC(code: string, id: string): SFC;
declare function getFileCodeAndLang(code: string, id: string): {
code: string;
lang: string;
};
declare function addNormalScript({
script,
lang
}: SFC, s: MagicString): {
start(): number;
end(): void;
};
declare function removeMacroImport(node: Node, s: MagicStringAST, offset: number): true | undefined;
//#endregion
export { BaseOptions, DEFINE_EMIT, DEFINE_EMITS, DEFINE_MODELS, DEFINE_MODELS_DOLLAR, DEFINE_OPTIONS, DEFINE_PROP, DEFINE_PROPS, DEFINE_PROPS_DOLLAR, DEFINE_PROPS_REFS, DEFINE_PROP_DOLLAR, DEFINE_RENDER, DEFINE_SETUP_COMPONENT, DEFINE_SLOTS, DEFINE_STYLEX, FilterFileType, FilterOptions, type FilterPattern, HELPER_PREFIX, MarkRequired, Overwrite, REGEX_NODE_MODULES, REGEX_SETUP_SFC, REGEX_SETUP_SFC_SUB, REGEX_SRC_FILE, REGEX_SUPPORTED_EXT, REGEX_VUE_SFC, REGEX_VUE_SUB, REGEX_VUE_SUB_SETUP, REPO_ISSUE_URL, RecordToUnion, SFC, SFCScriptBlock, TransformError, UnionToIntersection, VIRTUAL_ID_PREFIX, VuePluginApi, WITH_DEFAULTS, addNormalScript, checkInvalidScopeReference, createFilter, createRollupFilter, detectVueVersion, getFileCodeAndLang, getFilterPattern, getVuePluginApi, hackViteHMR, importHelperFn, isStaticExpression, isStaticObjectKey, normalizePath, parseSFC, removeMacroImport, resolveObjectExpression };

257
node_modules/@vue-macros/common/dist/index.js generated vendored Normal file
View File

@@ -0,0 +1,257 @@
import { babelParse, getLang, isFunctionType, isLiteralType, resolveObjectKey, resolveString } from "ast-kit";
import { createFilter as createFilter$1, createFilter as createRollupFilter, normalizePath } from "unplugin-utils";
import { parse, walkIdentifiers } from "@vue/compiler-sfc";
export * from "magic-string-ast"
export * from "ast-kit"
//#region src/ast.ts
function checkInvalidScopeReference(node, method, setupBindings) {
if (!node) return;
walkIdentifiers(node, (id) => {
if (setupBindings.includes(id.name)) throw new SyntaxError(`\`${method}()\` in <script setup> cannot reference locally declared variables (${id.name}) because it will be hoisted outside of the setup() function.`);
});
}
function isStaticExpression(node, options = {}) {
const { magicComment, fn, object, objectMethod, array, unary, regex } = options;
if (magicComment && node.leadingComments?.some((comment) => comment.value.trim() === magicComment)) return true;
else if (fn && isFunctionType(node)) return true;
switch (node.type) {
case "UnaryExpression": return !!unary && isStaticExpression(node.argument, options);
case "LogicalExpression":
case "BinaryExpression": return isStaticExpression(node.left, options) && isStaticExpression(node.right, options);
case "ConditionalExpression": return isStaticExpression(node.test, options) && isStaticExpression(node.consequent, options) && isStaticExpression(node.alternate, options);
case "SequenceExpression":
case "TemplateLiteral": return node.expressions.every((expr) => isStaticExpression(expr, options));
case "ArrayExpression": return !!array && node.elements.every((element) => element && isStaticExpression(element, options));
case "ObjectExpression": return !!object && node.properties.every((prop) => {
if (prop.type === "SpreadElement") return prop.argument.type === "ObjectExpression" && isStaticExpression(prop.argument, options);
else if (!isLiteralType(prop.key) && prop.computed) return false;
else if (prop.type === "ObjectProperty" && !isStaticExpression(prop.value, options)) return false;
if (prop.type === "ObjectMethod" && !objectMethod) return false;
return true;
});
case "ParenthesizedExpression":
case "TSNonNullExpression":
case "TSAsExpression":
case "TSTypeAssertion":
case "TSSatisfiesExpression": return isStaticExpression(node.expression, options);
case "RegExpLiteral": return !!regex;
}
if (isLiteralType(node)) return true;
return false;
}
function isStaticObjectKey(node) {
return node.properties.every((prop) => {
if (prop.type === "SpreadElement") return prop.argument.type === "ObjectExpression" && isStaticObjectKey(prop.argument);
return !prop.computed || isLiteralType(prop.key);
});
}
/**
* @param node must be a static expression, SpreadElement is not supported
*/
function resolveObjectExpression(node) {
const maps = Object.create(null);
for (const property of node.properties) if (property.type === "SpreadElement") {
if (property.argument.type !== "ObjectExpression") return void 0;
Object.assign(maps, resolveObjectExpression(property.argument));
} else {
const key = resolveObjectKey(property);
maps[key] = property;
}
return maps;
}
const importedMap = /* @__PURE__ */ new WeakMap();
const HELPER_PREFIX = "__MACROS_";
function importHelperFn(s, offset, imported, local = imported, from = "vue") {
const cacheKey = `${from}@${imported}`;
if (!importedMap.get(s)?.has(cacheKey)) {
s.appendLeft(offset, `\nimport ${imported === "default" ? HELPER_PREFIX + local : `{ ${imported} as ${HELPER_PREFIX + local} }`} from ${JSON.stringify(from)};`);
if (importedMap.has(s)) importedMap.get(s).add(cacheKey);
else importedMap.set(s, new Set([cacheKey]));
}
return `${HELPER_PREFIX}${local}`;
}
//#endregion
//#region src/constants.ts
const DEFINE_PROPS = "defineProps";
const DEFINE_PROPS_DOLLAR = "$defineProps";
const DEFINE_PROPS_REFS = "definePropsRefs";
const DEFINE_EMITS = "defineEmits";
const WITH_DEFAULTS = "withDefaults";
const DEFINE_OPTIONS = "defineOptions";
const DEFINE_MODELS = "defineModels";
const DEFINE_MODELS_DOLLAR = "$defineModels";
const DEFINE_SETUP_COMPONENT = "defineSetupComponent";
const DEFINE_RENDER = "defineRender";
const DEFINE_SLOTS = "defineSlots";
const DEFINE_STYLEX = "defineStyleX";
const DEFINE_PROP = "defineProp";
const DEFINE_PROP_DOLLAR = "$defineProp";
const DEFINE_EMIT = "defineEmit";
const REPO_ISSUE_URL = `https://github.com/vue-macros/vue-macros/issues`;
const REGEX_SRC_FILE = /\.[cm]?[jt]sx?$/;
const REGEX_SETUP_SFC = /\.setup\.[cm]?[jt]sx?$/;
const REGEX_SETUP_SFC_SUB = /\.setup\.[cm]?[jt]sx?((?!(?<!definePage&)vue&).)*$/;
const REGEX_VUE_SFC = /\.vue$/;
/** webpack only */
const REGEX_VUE_SUB = /\.vue(\.[tj]sx?)?\?vue&type=script/;
/** webpack only */
const REGEX_VUE_SUB_SETUP = /\.vue(\.[tj]sx?)?\?vue&type=script\b.+\bsetup=true/;
const REGEX_NODE_MODULES = /node_modules/;
const REGEX_SUPPORTED_EXT = /\.([cm]?[jt]sx?|vue)$/;
const VIRTUAL_ID_PREFIX = "/vue-macros";
//#endregion
//#region src/dep.ts
let require;
function getRequire() {
if (require) return require;
try {
if (globalThis.process?.getBuiltinModule) {
const module = process.getBuiltinModule("node:module");
if (module?.createRequire) return require = module.createRequire(import.meta.url);
}
} catch {}
}
function detectVueVersion(root, defaultVersion = 3.5) {
const require$1 = getRequire();
if (!require$1) {
console.warn(`Cannot detect Vue version. Default to Vue ${defaultVersion}`);
return defaultVersion;
}
const { resolve } = require$1("node:path");
const { statSync } = require$1("node:fs");
const { getPackageInfoSync } = require$1("local-pkg");
root ||= process.cwd();
let isFile = false;
try {
isFile = statSync(root).isFile();
} catch {}
const paths = [root];
if (!isFile) paths.unshift(resolve(root, "_index.js"));
const vuePkg = getPackageInfoSync("vue", { paths });
if (vuePkg && vuePkg.version) {
const version = Number.parseFloat(vuePkg.version);
return version >= 2 && version < 3 ? Math.trunc(version) : version;
} else return defaultVersion;
}
//#endregion
//#region src/error.ts
var TransformError = class extends SyntaxError {
constructor(message) {
super(message);
this.message = message;
this.name = "TransformError";
}
};
//#endregion
//#region src/unplugin.ts
function createFilter(options) {
return createFilter$1(options.include, options.exclude);
}
const VUE_PLUGINS = new Set(["vite:vue", "unplugin-vue"]);
function getVuePluginApi(plugins) {
const vuePlugin = (plugins || []).find((p) => VUE_PLUGINS.has(p.name));
if (!vuePlugin) throw new Error("Cannot find Vue plugin (@vitejs/plugin-vue or unplugin-vue). Please make sure to add it before using Vue Macros.");
const api = vuePlugin.api;
if (!api?.version) throw new Error("The Vue plugin is not supported (@vitejs/plugin-vue or unplugin-vue). Please make sure version > 4.3.4.");
return api;
}
let FilterFileType = /* @__PURE__ */ function(FilterFileType$1) {
/** Vue SFC */
FilterFileType$1[FilterFileType$1["VUE_SFC"] = 0] = "VUE_SFC";
/** Vue SFC with `<script setup>` */
FilterFileType$1[FilterFileType$1["VUE_SFC_WITH_SETUP"] = 1] = "VUE_SFC_WITH_SETUP";
/** foo.setup.tsx */
FilterFileType$1[FilterFileType$1["SETUP_SFC"] = 2] = "SETUP_SFC";
/** Source files */
FilterFileType$1[FilterFileType$1["SRC_FILE"] = 3] = "SRC_FILE";
return FilterFileType$1;
}({});
function getFilterPattern(types, framework) {
const filter = [];
const isWebpackLike = framework === "webpack" || framework === "rspack";
if (types.includes(FilterFileType.VUE_SFC)) filter.push(isWebpackLike ? REGEX_VUE_SUB : REGEX_VUE_SFC);
if (types.includes(FilterFileType.VUE_SFC_WITH_SETUP)) filter.push(isWebpackLike ? REGEX_VUE_SUB_SETUP : REGEX_VUE_SFC);
if (types.includes(FilterFileType.SETUP_SFC)) filter.push(REGEX_SETUP_SFC);
if (types.includes(FilterFileType.SRC_FILE)) filter.push(REGEX_SRC_FILE);
return filter;
}
function hackViteHMR(ctx, filter, callback) {
if (!filter(ctx.file)) return;
const { read } = ctx;
ctx.read = async () => {
const code = await read();
return (await callback(code, ctx.file))?.code || code;
};
}
//#endregion
//#region src/vue.ts
function parseSFC(code, id) {
const sfc = parse(code, { filename: id });
const { descriptor, errors } = sfc;
const scriptLang = sfc.descriptor.script?.lang;
const scriptSetupLang = sfc.descriptor.scriptSetup?.lang;
if (sfc.descriptor.script && sfc.descriptor.scriptSetup && (scriptLang || "js") !== (scriptSetupLang || "js")) throw new Error(`[vue-macros] <script> and <script setup> must have the same language type.`);
const lang = scriptLang || scriptSetupLang;
return Object.assign({}, descriptor, {
sfc,
lang,
errors,
offset: descriptor.scriptSetup?.loc.start.offset ?? 0,
getSetupAst() {
if (!descriptor.scriptSetup) return;
return babelParse(descriptor.scriptSetup.content, lang, {
plugins: [["importAttributes", { deprecatedAssertSyntax: true }]],
cache: true
});
},
getScriptAst() {
if (!descriptor.script) return;
return babelParse(descriptor.script.content, lang, {
plugins: [["importAttributes", { deprecatedAssertSyntax: true }]],
cache: true
});
}
});
}
function getFileCodeAndLang(code, id) {
if (!REGEX_VUE_SFC.test(id)) return {
code,
lang: getLang(id)
};
const sfc = parseSFC(code, id);
const scriptCode = sfc.script?.content ?? "";
return {
code: sfc.scriptSetup ? `${scriptCode}\n;\n${sfc.scriptSetup.content}` : scriptCode,
lang: sfc.lang ?? "js"
};
}
function addNormalScript({ script, lang }, s) {
return {
start() {
if (script) return script.loc.end.offset;
const attrs = lang ? ` lang="${lang}"` : "";
s.prependLeft(0, `<script${attrs}>`);
return 0;
},
end() {
if (!script) s.appendRight(0, `\n<\/script>\n`);
}
};
}
function removeMacroImport(node, s, offset) {
if (node.type === "ImportDeclaration" && node.attributes?.some((attr) => resolveString(attr.key) === "type" && attr.value.value === "macro")) {
s.removeNode(node, { offset });
return true;
}
}
//#endregion
export { DEFINE_EMIT, DEFINE_EMITS, DEFINE_MODELS, DEFINE_MODELS_DOLLAR, DEFINE_OPTIONS, DEFINE_PROP, DEFINE_PROPS, DEFINE_PROPS_DOLLAR, DEFINE_PROPS_REFS, DEFINE_PROP_DOLLAR, DEFINE_RENDER, DEFINE_SETUP_COMPONENT, DEFINE_SLOTS, DEFINE_STYLEX, FilterFileType, HELPER_PREFIX, REGEX_NODE_MODULES, REGEX_SETUP_SFC, REGEX_SETUP_SFC_SUB, REGEX_SRC_FILE, REGEX_SUPPORTED_EXT, REGEX_VUE_SFC, REGEX_VUE_SUB, REGEX_VUE_SUB_SETUP, REPO_ISSUE_URL, TransformError, VIRTUAL_ID_PREFIX, WITH_DEFAULTS, addNormalScript, checkInvalidScopeReference, createFilter, createRollupFilter, detectVueVersion, getFileCodeAndLang, getFilterPattern, getVuePluginApi, hackViteHMR, importHelperFn, isStaticExpression, isStaticObjectKey, normalizePath, parseSFC, removeMacroImport, resolveObjectExpression };

View File

@@ -0,0 +1,70 @@
MIT License
Copyright (c) Pooya Parsa <pooya@pi0.io> - Daniel Roe <daniel@roe.dev>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---
Copyright Joyent, Inc. and other Node contributors.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
---
Bundled zeptomatch (https://github.com/fabiospampinato/zeptomatch)
The MIT License (MIT)
Copyright (c) 2023-present Fabio Spampinato
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,73 @@
# 🛣️ pathe
> Universal filesystem path utils
[![version][npm-v-src]][npm-v-href]
[![downloads][npm-d-src]][npm-d-href]
[![size][size-src]][size-href]
## ❓ Why
For [historical reasons](https://docs.microsoft.com/en-us/archive/blogs/larryosterman/why-is-the-dos-path-character), windows followed MS-DOS and used backslash for separating paths rather than slash used for macOS, Linux, and other Posix operating systems. Nowadays, [Windows](https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file?redirectedfrom=MSDN) supports both Slash and Backslash for paths. [Node.js's built-in `path` module](https://nodejs.org/api/path.html) in the default operation of the path module varies based on the operating system on which a Node.js application is running. Specifically, when running on a Windows operating system, the path module will assume that Windows-style paths are being used. **This makes inconsistent code behavior between Windows and POSIX.**
Compared to popular [upath](https://github.com/anodynos/upath), pathe provides **identical exports** of Node.js with normalization on **all operations** and is written in modern **ESM/TypeScript** and has **no dependency on Node.js**!
This package is a drop-in replacement of the Node.js's [path module](https://nodejs.org/api/path.html) module and ensures paths are normalized with slash `/` and work in environments including Node.js.
## 💿 Usage
Install using npm or yarn:
```bash
# npm
npm i pathe
# yarn
yarn add pathe
# pnpm
pnpm i pathe
```
Import:
```js
// ESM / Typescript
import { resolve, matchesGlob } from "pathe";
// CommonJS
const { resolve, matchesGlob } = require("pathe");
```
Read more about path utils from [Node.js documentation](https://nodejs.org/api/path.html) and rest assured behavior is consistently like POSIX regardless of your input paths format and running platform (the only exception is `delimiter` constant export, it will be set to `;` on windows platform).
### Extra utilities
Pathe exports some extra utilities that do not exist in standard Node.js [path module](https://nodejs.org/api/path.html).
In order to use them, you can import from `pathe/utils` subpath:
```js
import {
filename,
normalizeAliases,
resolveAlias,
reverseResolveAlias,
} from "pathe/utils";
```
## License
Made with 💛 Published under the [MIT](./LICENSE) license.
Some code was used from the Node.js project. Glob supported is powered by [zeptomatch](https://github.com/fabiospampinato/zeptomatch).
<!-- Refs -->
[npm-v-src]: https://img.shields.io/npm/v/pathe?style=flat-square
[npm-v-href]: https://npmjs.com/package/pathe
[npm-d-src]: https://img.shields.io/npm/dm/pathe?style=flat-square
[npm-d-href]: https://npmjs.com/package/pathe
[github-actions-src]: https://img.shields.io/github/workflow/status/unjs/pathe/ci/main?style=flat-square
[github-actions-href]: https://github.com/unjs/pathe/actions?query=workflow%3Aci
[size-src]: https://packagephobia.now.sh/badge?p=pathe
[size-href]: https://packagephobia.now.sh/result?p=pathe

View File

@@ -0,0 +1,39 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const _path = require('./shared/pathe.BSlhyZSM.cjs');
const delimiter = /* @__PURE__ */ (() => globalThis.process?.platform === "win32" ? ";" : ":")();
const _platforms = { posix: void 0, win32: void 0 };
const mix = (del = delimiter) => {
return new Proxy(_path._path, {
get(_, prop) {
if (prop === "delimiter") return del;
if (prop === "posix") return posix;
if (prop === "win32") return win32;
return _platforms[prop] || _path._path[prop];
}
});
};
const posix = /* @__PURE__ */ mix(":");
const win32 = /* @__PURE__ */ mix(";");
exports.basename = _path.basename;
exports.dirname = _path.dirname;
exports.extname = _path.extname;
exports.format = _path.format;
exports.isAbsolute = _path.isAbsolute;
exports.join = _path.join;
exports.matchesGlob = _path.matchesGlob;
exports.normalize = _path.normalize;
exports.normalizeString = _path.normalizeString;
exports.parse = _path.parse;
exports.relative = _path.relative;
exports.resolve = _path.resolve;
exports.sep = _path.sep;
exports.toNamespacedPath = _path.toNamespacedPath;
exports.default = posix;
exports.delimiter = delimiter;
exports.posix = posix;
exports.win32 = win32;

View File

@@ -0,0 +1,47 @@
import * as path from 'node:path';
import path__default from 'node:path';
/**
* Constant for path separator.
*
* Always equals to `"/"`.
*/
declare const sep = "/";
declare const normalize: typeof path__default.normalize;
declare const join: typeof path__default.join;
declare const resolve: typeof path__default.resolve;
/**
* Resolves a string path, resolving '.' and '.' segments and allowing paths above the root.
*
* @param path - The path to normalise.
* @param allowAboveRoot - Whether to allow the resulting path to be above the root directory.
* @returns the normalised path string.
*/
declare function normalizeString(path: string, allowAboveRoot: boolean): string;
declare const isAbsolute: typeof path__default.isAbsolute;
declare const toNamespacedPath: typeof path__default.toNamespacedPath;
declare const extname: typeof path__default.extname;
declare const relative: typeof path__default.relative;
declare const dirname: typeof path__default.dirname;
declare const format: typeof path__default.format;
declare const basename: typeof path__default.basename;
declare const parse: typeof path__default.parse;
/**
* The `path.matchesGlob()` method determines if `path` matches the `pattern`.
* @param path The path to glob-match against.
* @param pattern The glob to check the path against.
*/
declare const matchesGlob: (path: string, pattern: string | string[]) => boolean;
type NodePath = typeof path;
/**
* The platform-specific file delimiter.
*
* Equals to `";"` in windows and `":"` in all other platforms.
*/
declare const delimiter: ";" | ":";
declare const posix: NodePath["posix"];
declare const win32: NodePath["win32"];
declare const _default: NodePath;
export { basename, _default as default, delimiter, dirname, extname, format, isAbsolute, join, matchesGlob, normalize, normalizeString, parse, posix, relative, resolve, sep, toNamespacedPath, win32 };

View File

@@ -0,0 +1,47 @@
import * as path from 'node:path';
import path__default from 'node:path';
/**
* Constant for path separator.
*
* Always equals to `"/"`.
*/
declare const sep = "/";
declare const normalize: typeof path__default.normalize;
declare const join: typeof path__default.join;
declare const resolve: typeof path__default.resolve;
/**
* Resolves a string path, resolving '.' and '.' segments and allowing paths above the root.
*
* @param path - The path to normalise.
* @param allowAboveRoot - Whether to allow the resulting path to be above the root directory.
* @returns the normalised path string.
*/
declare function normalizeString(path: string, allowAboveRoot: boolean): string;
declare const isAbsolute: typeof path__default.isAbsolute;
declare const toNamespacedPath: typeof path__default.toNamespacedPath;
declare const extname: typeof path__default.extname;
declare const relative: typeof path__default.relative;
declare const dirname: typeof path__default.dirname;
declare const format: typeof path__default.format;
declare const basename: typeof path__default.basename;
declare const parse: typeof path__default.parse;
/**
* The `path.matchesGlob()` method determines if `path` matches the `pattern`.
* @param path The path to glob-match against.
* @param pattern The glob to check the path against.
*/
declare const matchesGlob: (path: string, pattern: string | string[]) => boolean;
type NodePath = typeof path;
/**
* The platform-specific file delimiter.
*
* Equals to `";"` in windows and `":"` in all other platforms.
*/
declare const delimiter: ";" | ":";
declare const posix: NodePath["posix"];
declare const win32: NodePath["win32"];
declare const _default: NodePath;
export { basename, _default as default, delimiter, dirname, extname, format, isAbsolute, join, matchesGlob, normalize, normalizeString, parse, posix, relative, resolve, sep, toNamespacedPath, win32 };

View File

@@ -0,0 +1,47 @@
import * as path from 'node:path';
import path__default from 'node:path';
/**
* Constant for path separator.
*
* Always equals to `"/"`.
*/
declare const sep = "/";
declare const normalize: typeof path__default.normalize;
declare const join: typeof path__default.join;
declare const resolve: typeof path__default.resolve;
/**
* Resolves a string path, resolving '.' and '.' segments and allowing paths above the root.
*
* @param path - The path to normalise.
* @param allowAboveRoot - Whether to allow the resulting path to be above the root directory.
* @returns the normalised path string.
*/
declare function normalizeString(path: string, allowAboveRoot: boolean): string;
declare const isAbsolute: typeof path__default.isAbsolute;
declare const toNamespacedPath: typeof path__default.toNamespacedPath;
declare const extname: typeof path__default.extname;
declare const relative: typeof path__default.relative;
declare const dirname: typeof path__default.dirname;
declare const format: typeof path__default.format;
declare const basename: typeof path__default.basename;
declare const parse: typeof path__default.parse;
/**
* The `path.matchesGlob()` method determines if `path` matches the `pattern`.
* @param path The path to glob-match against.
* @param pattern The glob to check the path against.
*/
declare const matchesGlob: (path: string, pattern: string | string[]) => boolean;
type NodePath = typeof path;
/**
* The platform-specific file delimiter.
*
* Equals to `";"` in windows and `":"` in all other platforms.
*/
declare const delimiter: ";" | ":";
declare const posix: NodePath["posix"];
declare const win32: NodePath["win32"];
declare const _default: NodePath;
export { basename, _default as default, delimiter, dirname, extname, format, isAbsolute, join, matchesGlob, normalize, normalizeString, parse, posix, relative, resolve, sep, toNamespacedPath, win32 };

View File

@@ -0,0 +1,19 @@
import { _ as _path } from './shared/pathe.M-eThtNZ.mjs';
export { c as basename, d as dirname, e as extname, f as format, i as isAbsolute, j as join, m as matchesGlob, n as normalize, a as normalizeString, p as parse, b as relative, r as resolve, s as sep, t as toNamespacedPath } from './shared/pathe.M-eThtNZ.mjs';
const delimiter = /* @__PURE__ */ (() => globalThis.process?.platform === "win32" ? ";" : ":")();
const _platforms = { posix: void 0, win32: void 0 };
const mix = (del = delimiter) => {
return new Proxy(_path, {
get(_, prop) {
if (prop === "delimiter") return del;
if (prop === "posix") return posix;
if (prop === "win32") return win32;
return _platforms[prop] || _path[prop];
}
});
};
const posix = /* @__PURE__ */ mix(":");
const win32 = /* @__PURE__ */ mix(";");
export { posix as default, delimiter, posix, win32 };

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,82 @@
'use strict';
const _path = require('./shared/pathe.BSlhyZSM.cjs');
const pathSeparators = /* @__PURE__ */ new Set(["/", "\\", void 0]);
const normalizedAliasSymbol = Symbol.for("pathe:normalizedAlias");
const SLASH_RE = /[/\\]/;
function normalizeAliases(_aliases) {
if (_aliases[normalizedAliasSymbol]) {
return _aliases;
}
const aliases = Object.fromEntries(
Object.entries(_aliases).sort(([a], [b]) => _compareAliases(a, b))
);
for (const key in aliases) {
for (const alias in aliases) {
if (alias === key || key.startsWith(alias)) {
continue;
}
if (aliases[key]?.startsWith(alias) && pathSeparators.has(aliases[key][alias.length])) {
aliases[key] = aliases[alias] + aliases[key].slice(alias.length);
}
}
}
Object.defineProperty(aliases, normalizedAliasSymbol, {
value: true,
enumerable: false
});
return aliases;
}
function resolveAlias(path, aliases) {
const _path$1 = _path.normalizeWindowsPath(path);
aliases = normalizeAliases(aliases);
for (const [alias, to] of Object.entries(aliases)) {
if (!_path$1.startsWith(alias)) {
continue;
}
const _alias = hasTrailingSlash(alias) ? alias.slice(0, -1) : alias;
if (hasTrailingSlash(_path$1[_alias.length])) {
return _path.join(to, _path$1.slice(alias.length));
}
}
return _path$1;
}
function reverseResolveAlias(path, aliases) {
const _path$1 = _path.normalizeWindowsPath(path);
aliases = normalizeAliases(aliases);
const matches = [];
for (const [to, alias] of Object.entries(aliases)) {
if (!_path$1.startsWith(alias)) {
continue;
}
const _alias = hasTrailingSlash(alias) ? alias.slice(0, -1) : alias;
if (hasTrailingSlash(_path$1[_alias.length])) {
matches.push(_path.join(to, _path$1.slice(alias.length)));
}
}
return matches.sort((a, b) => b.length - a.length);
}
function filename(path) {
const base = path.split(SLASH_RE).pop();
if (!base) {
return void 0;
}
const separatorIndex = base.lastIndexOf(".");
if (separatorIndex <= 0) {
return base;
}
return base.slice(0, separatorIndex);
}
function _compareAliases(a, b) {
return b.split("/").length - a.split("/").length;
}
function hasTrailingSlash(path = "/") {
const lastChar = path[path.length - 1];
return lastChar === "/" || lastChar === "\\";
}
exports.filename = filename;
exports.normalizeAliases = normalizeAliases;
exports.resolveAlias = resolveAlias;
exports.reverseResolveAlias = reverseResolveAlias;

View File

@@ -0,0 +1,32 @@
/**
* Normalises alias mappings, ensuring that more specific aliases are resolved before less specific ones.
* This function also ensures that aliases do not resolve to themselves cyclically.
*
* @param _aliases - A set of alias mappings where each key is an alias and its value is the actual path it points to.
* @returns a set of normalised alias mappings.
*/
declare function normalizeAliases(_aliases: Record<string, string>): Record<string, string>;
/**
* Resolves a path string to its alias if applicable, otherwise returns the original path.
* This function normalises the path, resolves the alias and then joins it to the alias target if necessary.
*
* @param path - The path string to resolve.
* @param aliases - A set of alias mappings to use for resolution.
* @returns the resolved path as a string.
*/
declare function resolveAlias(path: string, aliases: Record<string, string>): string;
/**
* Resolves a path string to its possible alias.
*
* Returns an array of possible alias resolutions (could be empty), sorted by specificity (longest first).
*/
declare function reverseResolveAlias(path: string, aliases: Record<string, string>): string[];
/**
* Extracts the filename from a given path, excluding any directory paths and the file extension.
*
* @param path - The full path of the file from which to extract the filename.
* @returns the filename without the extension, or `undefined` if the filename cannot be extracted.
*/
declare function filename(path: string): string | undefined;
export { filename, normalizeAliases, resolveAlias, reverseResolveAlias };

View File

@@ -0,0 +1,32 @@
/**
* Normalises alias mappings, ensuring that more specific aliases are resolved before less specific ones.
* This function also ensures that aliases do not resolve to themselves cyclically.
*
* @param _aliases - A set of alias mappings where each key is an alias and its value is the actual path it points to.
* @returns a set of normalised alias mappings.
*/
declare function normalizeAliases(_aliases: Record<string, string>): Record<string, string>;
/**
* Resolves a path string to its alias if applicable, otherwise returns the original path.
* This function normalises the path, resolves the alias and then joins it to the alias target if necessary.
*
* @param path - The path string to resolve.
* @param aliases - A set of alias mappings to use for resolution.
* @returns the resolved path as a string.
*/
declare function resolveAlias(path: string, aliases: Record<string, string>): string;
/**
* Resolves a path string to its possible alias.
*
* Returns an array of possible alias resolutions (could be empty), sorted by specificity (longest first).
*/
declare function reverseResolveAlias(path: string, aliases: Record<string, string>): string[];
/**
* Extracts the filename from a given path, excluding any directory paths and the file extension.
*
* @param path - The full path of the file from which to extract the filename.
* @returns the filename without the extension, or `undefined` if the filename cannot be extracted.
*/
declare function filename(path: string): string | undefined;
export { filename, normalizeAliases, resolveAlias, reverseResolveAlias };

View File

@@ -0,0 +1,32 @@
/**
* Normalises alias mappings, ensuring that more specific aliases are resolved before less specific ones.
* This function also ensures that aliases do not resolve to themselves cyclically.
*
* @param _aliases - A set of alias mappings where each key is an alias and its value is the actual path it points to.
* @returns a set of normalised alias mappings.
*/
declare function normalizeAliases(_aliases: Record<string, string>): Record<string, string>;
/**
* Resolves a path string to its alias if applicable, otherwise returns the original path.
* This function normalises the path, resolves the alias and then joins it to the alias target if necessary.
*
* @param path - The path string to resolve.
* @param aliases - A set of alias mappings to use for resolution.
* @returns the resolved path as a string.
*/
declare function resolveAlias(path: string, aliases: Record<string, string>): string;
/**
* Resolves a path string to its possible alias.
*
* Returns an array of possible alias resolutions (could be empty), sorted by specificity (longest first).
*/
declare function reverseResolveAlias(path: string, aliases: Record<string, string>): string[];
/**
* Extracts the filename from a given path, excluding any directory paths and the file extension.
*
* @param path - The full path of the file from which to extract the filename.
* @returns the filename without the extension, or `undefined` if the filename cannot be extracted.
*/
declare function filename(path: string): string | undefined;
export { filename, normalizeAliases, resolveAlias, reverseResolveAlias };

View File

@@ -0,0 +1,77 @@
import { g as normalizeWindowsPath, j as join } from './shared/pathe.M-eThtNZ.mjs';
const pathSeparators = /* @__PURE__ */ new Set(["/", "\\", void 0]);
const normalizedAliasSymbol = Symbol.for("pathe:normalizedAlias");
const SLASH_RE = /[/\\]/;
function normalizeAliases(_aliases) {
if (_aliases[normalizedAliasSymbol]) {
return _aliases;
}
const aliases = Object.fromEntries(
Object.entries(_aliases).sort(([a], [b]) => _compareAliases(a, b))
);
for (const key in aliases) {
for (const alias in aliases) {
if (alias === key || key.startsWith(alias)) {
continue;
}
if (aliases[key]?.startsWith(alias) && pathSeparators.has(aliases[key][alias.length])) {
aliases[key] = aliases[alias] + aliases[key].slice(alias.length);
}
}
}
Object.defineProperty(aliases, normalizedAliasSymbol, {
value: true,
enumerable: false
});
return aliases;
}
function resolveAlias(path, aliases) {
const _path = normalizeWindowsPath(path);
aliases = normalizeAliases(aliases);
for (const [alias, to] of Object.entries(aliases)) {
if (!_path.startsWith(alias)) {
continue;
}
const _alias = hasTrailingSlash(alias) ? alias.slice(0, -1) : alias;
if (hasTrailingSlash(_path[_alias.length])) {
return join(to, _path.slice(alias.length));
}
}
return _path;
}
function reverseResolveAlias(path, aliases) {
const _path = normalizeWindowsPath(path);
aliases = normalizeAliases(aliases);
const matches = [];
for (const [to, alias] of Object.entries(aliases)) {
if (!_path.startsWith(alias)) {
continue;
}
const _alias = hasTrailingSlash(alias) ? alias.slice(0, -1) : alias;
if (hasTrailingSlash(_path[_alias.length])) {
matches.push(join(to, _path.slice(alias.length)));
}
}
return matches.sort((a, b) => b.length - a.length);
}
function filename(path) {
const base = path.split(SLASH_RE).pop();
if (!base) {
return void 0;
}
const separatorIndex = base.lastIndexOf(".");
if (separatorIndex <= 0) {
return base;
}
return base.slice(0, separatorIndex);
}
function _compareAliases(a, b) {
return b.split("/").length - a.split("/").length;
}
function hasTrailingSlash(path = "/") {
const lastChar = path[path.length - 1];
return lastChar === "/" || lastChar === "\\";
}
export { filename, normalizeAliases, resolveAlias, reverseResolveAlias };

View File

@@ -0,0 +1,61 @@
{
"name": "pathe",
"version": "2.0.3",
"description": "Universal filesystem path utils",
"repository": "unjs/pathe",
"license": "MIT",
"sideEffects": false,
"type": "module",
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
},
"./utils": {
"import": {
"types": "./dist/utils.d.mts",
"default": "./dist/utils.mjs"
},
"require": {
"types": "./dist/utils.d.cts",
"default": "./dist/utils.cjs"
}
}
},
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
"dist",
"utils.d.ts"
],
"devDependencies": {
"@types/node": "^22.13.1",
"@vitest/coverage-v8": "^3.0.5",
"changelogen": "^0.5.7",
"esbuild": "^0.25.0",
"eslint": "^9.20.1",
"eslint-config-unjs": "^0.4.2",
"jiti": "^2.4.2",
"prettier": "^3.5.0",
"typescript": "^5.7.3",
"unbuild": "^3.3.1",
"vitest": "^3.0.5",
"zeptomatch": "^2.0.0"
},
"scripts": {
"build": "unbuild",
"dev": "vitest",
"lint": "eslint . && prettier -c src test",
"lint:fix": "eslint . --fix && prettier -w src test",
"release": "pnpm test && pnpm build && changelogen --release && pnpm publish && git push --follow-tags",
"test": "pnpm lint && vitest run --coverage",
"test:types": "tsc --noEmit"
}
}

View File

@@ -0,0 +1 @@
export * from "./dist/utils";

View File

@@ -0,0 +1,43 @@
The MIT License (MIT)
Copyright © 2025-PRESENT Kevin Deng (https://github.com/sxzz)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
The MIT License (MIT)
Copyright (c) 2019 RollupJS Plugin Contributors (https://github.com/rollup/plugins/graphs/contributors)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,74 @@
# unplugin-utils
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![Unit Test][unit-test-src]][unit-test-href]
[![codecov][codecov-src]][codecov-href]
A set of utility functions commonly used by unplugins.
Thanks to [@rollup/pluginutils](https://github.com/rollup/plugins/tree/master/packages/pluginutils). This projects is heavily copied from it.
## Why Fork?
- 🌍 Platform agnostic, supports running in the browser, Node.js...
- ✂️ Subset, smaller bundle size.
- **💯 Coverage**: 100% test coverage.
## Install
```bash
npm i unplugin-utils
```
## Usage
### createFilter
```ts
export default function myPlugin(options = {}) {
const filter = createFilter(options.include, options.exclude)
return {
transform(code, id) {
if (!filter(id)) return
// proceed with the transformation...
},
}
}
```
### normalizePath
```ts
import { normalizePath } from 'unplugin-utils'
normalizePath(String.raw`foo\bar`) // 'foo/bar'
normalizePath('foo/bar') // 'foo/bar'
```
## Sponsors
<p align="center">
<a href="https://cdn.jsdelivr.net/gh/sxzz/sponsors/sponsors.svg">
<img src='https://cdn.jsdelivr.net/gh/sxzz/sponsors/sponsors.svg'/>
</a>
</p>
## License
[MIT](./LICENSE) License © 2025-PRESENT [Kevin Deng](https://github.com/sxzz)
[MIT](./LICENSE) Copyright (c) 2019 RollupJS Plugin Contributors (https://github.com/rollup/plugins/graphs/contributors)
<!-- Badges -->
[npm-version-src]: https://img.shields.io/npm/v/unplugin-utils.svg
[npm-version-href]: https://npmjs.com/package/unplugin-utils
[npm-downloads-src]: https://img.shields.io/npm/dm/unplugin-utils
[npm-downloads-href]: https://www.npmcharts.com/compare/unplugin-utils?interval=30
[unit-test-src]: https://github.com/sxzz/unplugin-utils/actions/workflows/unit-test.yml/badge.svg
[unit-test-href]: https://github.com/sxzz/unplugin-utils/actions/workflows/unit-test.yml
[codecov-src]: https://codecov.io/gh/sxzz/unplugin-utils/graph/badge.svg?token=VDWXCPSL1O
[codecov-href]: https://codecov.io/gh/sxzz/unplugin-utils

View File

@@ -0,0 +1,31 @@
//#region src/filter.d.ts
/**
* A valid `picomatch` glob pattern, or array of patterns.
*/
type FilterPattern = ReadonlyArray<string | RegExp> | string | RegExp | null;
/**
* Constructs a filter function which can be used to determine whether or not
* certain modules should be operated upon.
* @param include If `include` is omitted or has zero length, filter will return `true` by default.
* @param exclude ID must not match any of the `exclude` patterns.
* @param options Additional options.
* @param options.resolve Optionally resolves the patterns against a directory other than `process.cwd()`.
* If a `string` is specified, then the value will be used as the base directory.
* Relative paths will be resolved against `process.cwd()` first.
* If `false`, then the patterns will not be resolved against any directory.
* This can be useful if you want to create a filter for virtual module names.
*/
declare function createFilter(include?: FilterPattern, exclude?: FilterPattern, options?: {
resolve?: string | false | null;
}): (id: string | unknown) => boolean;
//#endregion
//#region src/path.d.ts
/**
* Converts path separators to forward slash.
*/
declare function normalizePath(filename: string): string;
//#endregion
//#region src/utils.d.ts
declare function toArray<T>(thing: readonly T[] | T | undefined | null): readonly T[];
//#endregion
export { FilterPattern, createFilter, normalizePath, toArray };

View File

@@ -0,0 +1,67 @@
import { isAbsolute, join, resolve } from "pathe";
import pm from "picomatch";
//#region src/path.ts
/**
* Converts path separators to forward slash.
*/
function normalizePath(filename) {
return filename.replaceAll("\\", "/");
}
//#endregion
//#region src/utils.ts
const isArray = Array.isArray;
function toArray(thing) {
if (isArray(thing)) return thing;
if (thing == null) return [];
return [thing];
}
//#endregion
//#region src/filter.ts
const escapeMark = "[_#EsCaPe#_]";
function getMatcherString(id, resolutionBase) {
if (resolutionBase === false || isAbsolute(id) || id.startsWith("**")) return normalizePath(id);
const basePath = normalizePath(resolve(resolutionBase || "")).replaceAll(/[-^$*+?.()|[\]{}]/g, `${escapeMark}$&`);
return join(basePath, normalizePath(id)).replaceAll(escapeMark, "\\");
}
/**
* Constructs a filter function which can be used to determine whether or not
* certain modules should be operated upon.
* @param include If `include` is omitted or has zero length, filter will return `true` by default.
* @param exclude ID must not match any of the `exclude` patterns.
* @param options Additional options.
* @param options.resolve Optionally resolves the patterns against a directory other than `process.cwd()`.
* If a `string` is specified, then the value will be used as the base directory.
* Relative paths will be resolved against `process.cwd()` first.
* If `false`, then the patterns will not be resolved against any directory.
* This can be useful if you want to create a filter for virtual module names.
*/
function createFilter(include, exclude, options) {
const resolutionBase = options && options.resolve;
const getMatcher = (id) => id instanceof RegExp ? id : { test: (what) => {
const pattern = getMatcherString(id, resolutionBase);
return pm(pattern, { dot: true })(what);
} };
const includeMatchers = toArray(include).map(getMatcher);
const excludeMatchers = toArray(exclude).map(getMatcher);
if (!includeMatchers.length && !excludeMatchers.length) return (id) => typeof id === "string" && !id.includes("\0");
return function result(id) {
if (typeof id !== "string") return false;
if (id.includes("\0")) return false;
const pathId = normalizePath(id);
for (const matcher of excludeMatchers) {
if (matcher instanceof RegExp) matcher.lastIndex = 0;
if (matcher.test(pathId)) return false;
}
for (const matcher of includeMatchers) {
if (matcher instanceof RegExp) matcher.lastIndex = 0;
if (matcher.test(pathId)) return true;
}
return !includeMatchers.length;
};
}
//#endregion
export { createFilter, normalizePath, toArray };

View File

@@ -0,0 +1,67 @@
{
"name": "unplugin-utils",
"version": "0.3.1",
"description": "A set of utility functions commonly used by unplugins.",
"type": "module",
"license": "MIT",
"homepage": "https://github.com/sxzz/unplugin-utils#readme",
"bugs": {
"url": "https://github.com/sxzz/unplugin-utils/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/sxzz/unplugin-utils.git"
},
"author": "Kevin Deng <sxzz@sxzz.moe>",
"funding": "https://github.com/sponsors/sxzz",
"files": [
"dist"
],
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": "./dist/index.js",
"./package.json": "./package.json"
},
"publishConfig": {
"access": "public"
},
"dependencies": {
"pathe": "^2.0.3",
"picomatch": "^4.0.3"
},
"devDependencies": {
"@sxzz/eslint-config": "^7.2.7",
"@sxzz/prettier-config": "^2.2.4",
"@types/node": "^24.7.0",
"@types/picomatch": "^4.0.2",
"@vitest/coverage-v8": "3.2.4",
"bumpp": "^10.3.1",
"eslint": "^9.37.0",
"oxc-transform": "^0.94.0",
"prettier": "^3.6.2",
"tsdown": "^0.15.6",
"tsx": "^4.20.6",
"typescript": "^5.9.3",
"vitest": "^3.2.4"
},
"engines": {
"node": ">=20.19.0"
},
"prettier": "@sxzz/prettier-config",
"tsdown": {
"platform": "neutral",
"exports": true
},
"scripts": {
"lint": "eslint --cache .",
"lint:fix": "pnpm run lint --fix",
"build": "tsdown",
"dev": "tsdown --watch",
"test": "vitest",
"typecheck": "tsc --noEmit",
"format": "prettier --cache --write .",
"release": "bumpp"
}
}

63
node_modules/@vue-macros/common/package.json generated vendored Normal file
View File

@@ -0,0 +1,63 @@
{
"name": "@vue-macros/common",
"version": "3.1.2",
"description": "common feature from Vue Macros.",
"type": "module",
"keywords": [
"vue-macros",
"macros",
"vue",
"sfc",
"setup",
"script-setup",
"common"
],
"license": "MIT",
"homepage": "https://vue-macros.dev",
"bugs": {
"url": "https://github.com/vue-macros/vue-macros/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/vue-macros/vue-macros.git",
"directory": "packages/common"
},
"author": "Kevin Deng <sxzz@sxzz.moe>",
"funding": "https://github.com/sponsors/vue-macros",
"files": [
"dist"
],
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": "./dist/index.js",
"./*": "./*"
},
"publishConfig": {
"access": "public"
},
"peerDependencies": {
"vue": "^2.7.0 || ^3.2.25"
},
"peerDependenciesMeta": {
"vue": {
"optional": true
}
},
"dependencies": {
"@vue/compiler-sfc": "^3.5.22",
"ast-kit": "^2.1.2",
"local-pkg": "^1.1.2",
"magic-string-ast": "^1.0.2",
"unplugin-utils": "^0.3.0"
},
"devDependencies": {
"@babel/parser": "^7.28.4",
"@vitejs/plugin-vue": "^6.0.1"
},
"engines": {
"node": ">=20.19.0"
},
"scripts": {}
}