vite-plugin-glob-guard 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +73 -0
- package/dist/index.cjs +460 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +26 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +427 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# vite-plugin-glob-guard
|
|
2
|
+
|
|
3
|
+
Fail the build when a required `import.meta.glob(...)` matches zero files.
|
|
4
|
+
|
|
5
|
+
## What it is
|
|
6
|
+
|
|
7
|
+
When a glob pattern stops matching (rename/move, wrong extension, negation patterns, etc.),
|
|
8
|
+
Vite typically does not fail the build; you can end up with an empty object `{}`.
|
|
9
|
+
|
|
10
|
+
This plugin makes that failure mode opt-in and explicit: mark a glob call site as
|
|
11
|
+
required and fail the build if it matches zero files.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm i -D vite-plugin-glob-guard
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
Vite config:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// vite.config.ts
|
|
25
|
+
import { defineConfig } from 'vite'
|
|
26
|
+
import globGuard from 'vite-plugin-glob-guard'
|
|
27
|
+
|
|
28
|
+
export default defineConfig({
|
|
29
|
+
plugins: [globGuard()],
|
|
30
|
+
})
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Mark required call sites:
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
// glob-guard:required
|
|
37
|
+
const posts = import.meta.glob('./posts/*.md', { eager: true })
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Options
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
export interface GlobGuardOptions {
|
|
44
|
+
mode?: 'annotated' | 'all'
|
|
45
|
+
failOnBuild?: boolean
|
|
46
|
+
report?: false | true | { file: string }
|
|
47
|
+
ignoreImporters?: (string | RegExp)[]
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Defaults:
|
|
52
|
+
|
|
53
|
+
- `mode: 'annotated'`
|
|
54
|
+
- `failOnBuild: true`
|
|
55
|
+
- `report: false`
|
|
56
|
+
|
|
57
|
+
## Example error
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
[glob-guard] Required glob matched 0 files
|
|
61
|
+
importer: src/content.ts:12:5
|
|
62
|
+
glob: ./posts/*.md
|
|
63
|
+
resolved:
|
|
64
|
+
- /abs/path/to/project/src/posts/*.md
|
|
65
|
+
hints:
|
|
66
|
+
- Did you rename/move the directory?
|
|
67
|
+
- Did you change file extensions?
|
|
68
|
+
- Are you excluding everything via a negated pattern ("!...")?
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Notes
|
|
72
|
+
|
|
73
|
+
- Vite 7+ only.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
default: () => globGuard
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/parse.ts
|
|
38
|
+
var import_parser = require("@babel/parser");
|
|
39
|
+
var import_traverse = __toESM(require("@babel/traverse"), 1);
|
|
40
|
+
var REQUIRED_TOKEN = "glob-guard:required";
|
|
41
|
+
function hasRequiredAnnotation(comments) {
|
|
42
|
+
if (!comments || comments.length === 0) return false;
|
|
43
|
+
return comments.some((c) => c.value.includes(REQUIRED_TOKEN));
|
|
44
|
+
}
|
|
45
|
+
function isImportMetaGlobCallee(node) {
|
|
46
|
+
if (node.type !== "MemberExpression") return false;
|
|
47
|
+
if (node.computed) return false;
|
|
48
|
+
const object = node.object;
|
|
49
|
+
const property = node.property;
|
|
50
|
+
if (object.type !== "MetaProperty") return false;
|
|
51
|
+
if (object.meta.type !== "Identifier" || object.meta.name !== "import") return false;
|
|
52
|
+
if (object.property.type !== "Identifier" || object.property.name !== "meta")
|
|
53
|
+
return false;
|
|
54
|
+
return property.type === "Identifier" && property.name === "glob";
|
|
55
|
+
}
|
|
56
|
+
function readStringLiteral(node) {
|
|
57
|
+
if (node.type === "StringLiteral") return node.value;
|
|
58
|
+
if (node.type === "TemplateLiteral") {
|
|
59
|
+
if (node.expressions.length !== 0) return null;
|
|
60
|
+
return node.quasis[0]?.value?.raw ?? "";
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
function readGlobsArg(node) {
|
|
65
|
+
const single = readStringLiteral(node);
|
|
66
|
+
if (single != null) return [single];
|
|
67
|
+
if (node.type !== "ArrayExpression") return null;
|
|
68
|
+
const globs = [];
|
|
69
|
+
for (const el of node.elements) {
|
|
70
|
+
if (!el) continue;
|
|
71
|
+
if (el.type === "SpreadElement") return null;
|
|
72
|
+
const s = readStringLiteral(el);
|
|
73
|
+
if (s == null) return null;
|
|
74
|
+
globs.push(s);
|
|
75
|
+
}
|
|
76
|
+
return globs;
|
|
77
|
+
}
|
|
78
|
+
function readOptions(node) {
|
|
79
|
+
if (!node) return {};
|
|
80
|
+
if (node.type !== "ObjectExpression") return {};
|
|
81
|
+
const out = {};
|
|
82
|
+
for (const prop of node.properties) {
|
|
83
|
+
if (prop.type !== "ObjectProperty") continue;
|
|
84
|
+
if (prop.computed) continue;
|
|
85
|
+
if (prop.key.type !== "Identifier" && prop.key.type !== "StringLiteral") continue;
|
|
86
|
+
const key = prop.key.type === "Identifier" ? prop.key.name : prop.key.value;
|
|
87
|
+
if (key === "exhaustive" && prop.value.type === "BooleanLiteral") {
|
|
88
|
+
out.exhaustive = prop.value.value;
|
|
89
|
+
}
|
|
90
|
+
if (key === "base") {
|
|
91
|
+
const base = readStringLiteral(prop.value);
|
|
92
|
+
if (base != null) out.base = base;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
function parseRequiredGlobCalls(code, importer, options) {
|
|
98
|
+
if (!code.includes("import.meta.glob")) return [];
|
|
99
|
+
if (options.mode === "annotated" && !code.includes(REQUIRED_TOKEN)) return [];
|
|
100
|
+
const ast = (0, import_parser.parse)(code, {
|
|
101
|
+
sourceType: "module",
|
|
102
|
+
sourceFilename: importer,
|
|
103
|
+
plugins: ["typescript", "jsx"],
|
|
104
|
+
errorRecovery: true,
|
|
105
|
+
ranges: true,
|
|
106
|
+
attachComment: true
|
|
107
|
+
});
|
|
108
|
+
const results = [];
|
|
109
|
+
(0, import_traverse.default)(ast, {
|
|
110
|
+
CallExpression(path) {
|
|
111
|
+
if (!isImportMetaGlobCallee(path.node.callee)) return;
|
|
112
|
+
const args = path.node.arguments;
|
|
113
|
+
if (args.length < 1) return;
|
|
114
|
+
const arg0 = args[0];
|
|
115
|
+
const arg1 = args[1];
|
|
116
|
+
const globNode = arg0;
|
|
117
|
+
const optsNode = arg1 ?? void 0;
|
|
118
|
+
const globs = readGlobsArg(globNode);
|
|
119
|
+
if (!globs || globs.length === 0) return;
|
|
120
|
+
const required = options.mode === "all" || (() => {
|
|
121
|
+
const stmt = path.getStatementParent()?.node;
|
|
122
|
+
if (!stmt) return false;
|
|
123
|
+
return hasRequiredAnnotation(stmt.leadingComments) || hasRequiredAnnotation(stmt.trailingComments) || hasRequiredAnnotation(path.node.leadingComments) || hasRequiredAnnotation(path.node.trailingComments);
|
|
124
|
+
})();
|
|
125
|
+
if (!required) return;
|
|
126
|
+
const opts = readOptions(optsNode);
|
|
127
|
+
results.push({
|
|
128
|
+
importer,
|
|
129
|
+
globs,
|
|
130
|
+
options: opts,
|
|
131
|
+
loc: path.node.loc ? { line: path.node.loc.start.line, column: path.node.loc.start.column } : null
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
return results;
|
|
136
|
+
}
|
|
137
|
+
function getLineAndColumn(source, index) {
|
|
138
|
+
const prefix = source.slice(0, index);
|
|
139
|
+
const lastNewline = prefix.lastIndexOf("\n");
|
|
140
|
+
const line = prefix.split("\n").length;
|
|
141
|
+
const column = lastNewline === -1 ? prefix.length : prefix.length - lastNewline - 1;
|
|
142
|
+
return { line, column };
|
|
143
|
+
}
|
|
144
|
+
function offsetLoc(loc, startLine, startColumn) {
|
|
145
|
+
if (!loc) return null;
|
|
146
|
+
const line = startLine + loc.line - 1;
|
|
147
|
+
const column = loc.line === 1 ? startColumn + loc.column : loc.column;
|
|
148
|
+
return { line, column };
|
|
149
|
+
}
|
|
150
|
+
function parseRequiredGlobCallsInScriptTags(code, importer, options) {
|
|
151
|
+
if (!code.includes("<script")) return [];
|
|
152
|
+
if (!code.includes("import.meta.glob")) return [];
|
|
153
|
+
if (options.mode === "annotated" && !code.includes(REQUIRED_TOKEN)) return [];
|
|
154
|
+
const results = [];
|
|
155
|
+
let cursor = 0;
|
|
156
|
+
while (cursor < code.length) {
|
|
157
|
+
const open = code.indexOf("<script", cursor);
|
|
158
|
+
if (open === -1) break;
|
|
159
|
+
const openEnd = code.indexOf(">", open);
|
|
160
|
+
if (openEnd === -1) break;
|
|
161
|
+
const close = code.indexOf("</script>", openEnd + 1);
|
|
162
|
+
if (close === -1) break;
|
|
163
|
+
const contentStart = openEnd + 1;
|
|
164
|
+
const contentEnd = close;
|
|
165
|
+
const content = code.slice(contentStart, contentEnd);
|
|
166
|
+
const start = getLineAndColumn(code, contentStart);
|
|
167
|
+
const callsites = parseRequiredGlobCalls(content, importer, options);
|
|
168
|
+
for (const cs of callsites) {
|
|
169
|
+
results.push({
|
|
170
|
+
...cs,
|
|
171
|
+
loc: offsetLoc(cs.loc, start.line, start.column)
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
cursor = close + "</script>".length;
|
|
175
|
+
}
|
|
176
|
+
return results;
|
|
177
|
+
}
|
|
178
|
+
function parseRequiredGlobCallsInAstroFrontmatter(code, importer, options) {
|
|
179
|
+
if (!code.startsWith("---")) return [];
|
|
180
|
+
if (!code.includes("import.meta.glob")) return [];
|
|
181
|
+
if (options.mode === "annotated" && !code.includes(REQUIRED_TOKEN)) return [];
|
|
182
|
+
const openNewline = code.indexOf("\n");
|
|
183
|
+
if (openNewline === -1) return [];
|
|
184
|
+
const contentStart = openNewline + 1;
|
|
185
|
+
const fmMatch = code.match(/^---\s*\r?\n([\s\S]*?)\r?\n---\s*(?:\r?\n|$)/);
|
|
186
|
+
if (!fmMatch) return [];
|
|
187
|
+
const frontmatter = fmMatch[1] ?? "";
|
|
188
|
+
const start = getLineAndColumn(code, contentStart);
|
|
189
|
+
const callsites = parseRequiredGlobCalls(frontmatter, importer, options);
|
|
190
|
+
return callsites.map((cs) => ({
|
|
191
|
+
...cs,
|
|
192
|
+
loc: offsetLoc(cs.loc, start.line, start.column)
|
|
193
|
+
}));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// src/utils.ts
|
|
197
|
+
function slash(p) {
|
|
198
|
+
return p.replace(/\\/g, "/");
|
|
199
|
+
}
|
|
200
|
+
function stripQuery(id) {
|
|
201
|
+
const i = id.indexOf("?");
|
|
202
|
+
return i === -1 ? id : id.slice(0, i);
|
|
203
|
+
}
|
|
204
|
+
function matchesAny(value, patterns) {
|
|
205
|
+
if (!patterns || patterns.length === 0) return false;
|
|
206
|
+
return patterns.some((p) => typeof p === "string" ? p === value : p.test(value));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/resolve.ts
|
|
210
|
+
var import_node_path = require("path");
|
|
211
|
+
var import_picomatch = __toESM(require("picomatch"), 1);
|
|
212
|
+
var import_tinyglobby = require("tinyglobby");
|
|
213
|
+
function globSafePath(p) {
|
|
214
|
+
return (0, import_tinyglobby.escapePath)(slash(p));
|
|
215
|
+
}
|
|
216
|
+
function lastNthChar(str, n) {
|
|
217
|
+
return str.charAt(str.length - 1 - n);
|
|
218
|
+
}
|
|
219
|
+
function globSafeResolvedPath(resolved, glob) {
|
|
220
|
+
let numEqual = 0;
|
|
221
|
+
const maxEqual = Math.min(resolved.length, glob.length);
|
|
222
|
+
while (numEqual < maxEqual && lastNthChar(resolved, numEqual) === lastNthChar(glob, numEqual)) {
|
|
223
|
+
numEqual += 1;
|
|
224
|
+
}
|
|
225
|
+
const staticPartEnd = resolved.length - numEqual;
|
|
226
|
+
const staticPart = resolved.slice(0, staticPartEnd);
|
|
227
|
+
const dynamicPart = resolved.slice(staticPartEnd);
|
|
228
|
+
return globSafePath(staticPart) + dynamicPart;
|
|
229
|
+
}
|
|
230
|
+
async function toAbsoluteGlob(glob, ctx) {
|
|
231
|
+
let pre = "";
|
|
232
|
+
if (glob[0] === "!") {
|
|
233
|
+
pre = "!";
|
|
234
|
+
glob = glob.slice(1);
|
|
235
|
+
}
|
|
236
|
+
const root = globSafePath(ctx.root);
|
|
237
|
+
let dir;
|
|
238
|
+
if (ctx.base) {
|
|
239
|
+
if (ctx.base[0] === "/") {
|
|
240
|
+
dir = import_node_path.posix.join(root, ctx.base);
|
|
241
|
+
} else {
|
|
242
|
+
dir = import_node_path.posix.resolve(globSafePath(import_node_path.posix.dirname(ctx.importer)), ctx.base);
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
dir = globSafePath(import_node_path.posix.dirname(ctx.importer));
|
|
246
|
+
}
|
|
247
|
+
if (glob[0] === "/") return pre + import_node_path.posix.join(root, glob.slice(1));
|
|
248
|
+
if (glob.startsWith("./")) return pre + import_node_path.posix.join(dir, glob.slice(2));
|
|
249
|
+
if (glob.startsWith("../")) return pre + import_node_path.posix.join(dir, glob);
|
|
250
|
+
if (glob.startsWith("**")) return pre + glob;
|
|
251
|
+
const resolved = slash(await ctx.resolveId(glob, ctx.importer) || glob);
|
|
252
|
+
if ((0, import_node_path.isAbsolute)(resolved)) {
|
|
253
|
+
return pre + globSafeResolvedPath(resolved, glob);
|
|
254
|
+
}
|
|
255
|
+
throw new Error(
|
|
256
|
+
`Invalid glob: "${glob}" (resolved: "${resolved}"). It must start with '/' or './'`
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
function getCommonBase(globsResolved) {
|
|
260
|
+
const bases = globsResolved.filter((g) => g[0] !== "!").map((glob) => {
|
|
261
|
+
let { base } = import_picomatch.default.scan(glob);
|
|
262
|
+
if (import_node_path.posix.basename(base).includes(".")) base = import_node_path.posix.dirname(base);
|
|
263
|
+
return base;
|
|
264
|
+
});
|
|
265
|
+
if (!bases.length) return null;
|
|
266
|
+
let commonAncestor = "";
|
|
267
|
+
const parts = bases[0].split("/");
|
|
268
|
+
for (let i = 0; i < parts.length; i++) {
|
|
269
|
+
const candidate = parts.slice(0, i + 1).join("/");
|
|
270
|
+
if (bases.every((b) => b.startsWith(candidate))) commonAncestor = candidate;
|
|
271
|
+
else break;
|
|
272
|
+
}
|
|
273
|
+
if (!commonAncestor) commonAncestor = "/";
|
|
274
|
+
return commonAncestor;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// src/match.ts
|
|
278
|
+
var import_tinyglobby2 = require("tinyglobby");
|
|
279
|
+
async function matchGlobs(input) {
|
|
280
|
+
const globsResolved = input.globsResolved.map(slash);
|
|
281
|
+
const root = slash(input.root);
|
|
282
|
+
const importerFile = slash(input.importerFile);
|
|
283
|
+
const cwd = getCommonBase(globsResolved) ?? root;
|
|
284
|
+
const dot = !!input.exhaustive;
|
|
285
|
+
const ignore = dot ? [] : ["**/node_modules/**"];
|
|
286
|
+
const matches = (await (0, import_tinyglobby2.glob)(globsResolved, {
|
|
287
|
+
cwd,
|
|
288
|
+
absolute: true,
|
|
289
|
+
dot,
|
|
290
|
+
expandDirectories: false,
|
|
291
|
+
ignore
|
|
292
|
+
})).map(slash).filter((f) => f !== importerFile).sort();
|
|
293
|
+
return {
|
|
294
|
+
cwd,
|
|
295
|
+
dot,
|
|
296
|
+
ignore,
|
|
297
|
+
matches
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// src/diagnostics.ts
|
|
302
|
+
function formatEmptyGlobFailure(f) {
|
|
303
|
+
const importerLabel = f.loc != null ? `${slash(f.importerFile)}:${f.loc.line}:${f.loc.column + 1}` : slash(f.importerFile);
|
|
304
|
+
const lines = [];
|
|
305
|
+
lines.push("[glob-guard] Required glob matched 0 files");
|
|
306
|
+
lines.push(` importer: ${importerLabel}`);
|
|
307
|
+
if (f.globs.length === 1) {
|
|
308
|
+
lines.push(` glob: ${f.globs[0]}`);
|
|
309
|
+
} else {
|
|
310
|
+
lines.push(" globs:");
|
|
311
|
+
for (const g of f.globs) lines.push(` - ${g}`);
|
|
312
|
+
}
|
|
313
|
+
lines.push(" resolved:");
|
|
314
|
+
for (const g of f.globsResolved) lines.push(` - ${g}`);
|
|
315
|
+
lines.push(` options: exhaustive=${String(f.exhaustive)}${f.base ? ` base=${f.base}` : ""}`);
|
|
316
|
+
lines.push(` cwd: ${f.cwd}`);
|
|
317
|
+
if (f.ignore.length > 0) {
|
|
318
|
+
lines.push(" ignore:");
|
|
319
|
+
for (const i of f.ignore) lines.push(` - ${i}`);
|
|
320
|
+
}
|
|
321
|
+
lines.push(" hints:");
|
|
322
|
+
lines.push(" - Did you rename/move the directory?");
|
|
323
|
+
lines.push(" - Did you change file extensions?");
|
|
324
|
+
lines.push(' - Are you excluding everything via a negated pattern ("!...")?');
|
|
325
|
+
const err = new Error(lines.join("\n"));
|
|
326
|
+
err.id = slash(f.importerFile);
|
|
327
|
+
if (f.loc) err.loc = { file: err.id, line: f.loc.line, column: f.loc.column };
|
|
328
|
+
return err;
|
|
329
|
+
}
|
|
330
|
+
function formatManyFailures(failures) {
|
|
331
|
+
if (failures.length === 1) return formatEmptyGlobFailure(failures[0]);
|
|
332
|
+
const lines = [];
|
|
333
|
+
lines.push(`[glob-guard] ${failures.length} required globs matched 0 files`);
|
|
334
|
+
for (const f of failures) {
|
|
335
|
+
const importerLabel = f.loc != null ? `${slash(f.importerFile)}:${f.loc.line}:${f.loc.column + 1}` : slash(f.importerFile);
|
|
336
|
+
lines.push(` - ${importerLabel}`);
|
|
337
|
+
if (f.globs.length === 1) {
|
|
338
|
+
lines.push(` glob: ${f.globs[0]}`);
|
|
339
|
+
} else {
|
|
340
|
+
lines.push(" globs:");
|
|
341
|
+
for (const g of f.globs) lines.push(` - ${g}`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
const err = new Error(lines.join("\n"));
|
|
345
|
+
return err;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// src/index.ts
|
|
349
|
+
var DEFAULTS = {
|
|
350
|
+
mode: "annotated",
|
|
351
|
+
failOnBuild: true,
|
|
352
|
+
checkOnServe: false,
|
|
353
|
+
failOnServe: false,
|
|
354
|
+
report: false,
|
|
355
|
+
ignoreImporters: []
|
|
356
|
+
};
|
|
357
|
+
function globGuard(userOptions = {}) {
|
|
358
|
+
const options = { ...DEFAULTS, ...userOptions };
|
|
359
|
+
let root = "";
|
|
360
|
+
let isBuild = false;
|
|
361
|
+
const callsites = [];
|
|
362
|
+
async function checkAll() {
|
|
363
|
+
if (callsites.length === 0) return;
|
|
364
|
+
const failures = [];
|
|
365
|
+
for (const cs of callsites) {
|
|
366
|
+
const importerFile = stripQuery(cs.importer);
|
|
367
|
+
const globsResolved = await Promise.all(
|
|
368
|
+
cs.globs.map(
|
|
369
|
+
(g) => toAbsoluteGlob(g, {
|
|
370
|
+
root,
|
|
371
|
+
importer: slash(importerFile),
|
|
372
|
+
base: cs.options.base,
|
|
373
|
+
resolveId: async (id, importer) => {
|
|
374
|
+
const r = await this.resolve(id, importer);
|
|
375
|
+
return r?.id;
|
|
376
|
+
}
|
|
377
|
+
})
|
|
378
|
+
)
|
|
379
|
+
);
|
|
380
|
+
const match = await matchGlobs({
|
|
381
|
+
globsResolved,
|
|
382
|
+
root,
|
|
383
|
+
importerFile,
|
|
384
|
+
exhaustive: cs.options.exhaustive
|
|
385
|
+
});
|
|
386
|
+
if (match.matches.length === 0) {
|
|
387
|
+
failures.push({
|
|
388
|
+
importer: cs.importer,
|
|
389
|
+
importerFile,
|
|
390
|
+
loc: cs.loc,
|
|
391
|
+
globs: cs.globs,
|
|
392
|
+
globsResolved,
|
|
393
|
+
exhaustive: !!cs.options.exhaustive,
|
|
394
|
+
base: cs.options.base,
|
|
395
|
+
cwd: match.cwd,
|
|
396
|
+
ignore: match.ignore
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
if (failures.length === 0) return;
|
|
401
|
+
if (options.report) {
|
|
402
|
+
const reportFile = options.report === true ? "glob-guard.report.json" : options.report.file;
|
|
403
|
+
const fs = await import("fs/promises");
|
|
404
|
+
const payload = {
|
|
405
|
+
root,
|
|
406
|
+
failures: failures.map((f) => ({
|
|
407
|
+
importer: slash(f.importerFile),
|
|
408
|
+
loc: f.loc,
|
|
409
|
+
globs: f.globs,
|
|
410
|
+
globsResolved: f.globsResolved,
|
|
411
|
+
exhaustive: f.exhaustive,
|
|
412
|
+
base: f.base,
|
|
413
|
+
cwd: f.cwd,
|
|
414
|
+
ignore: f.ignore
|
|
415
|
+
}))
|
|
416
|
+
};
|
|
417
|
+
await fs.writeFile(reportFile, JSON.stringify(payload, null, 2), "utf8");
|
|
418
|
+
}
|
|
419
|
+
const err = formatManyFailures(failures);
|
|
420
|
+
if (isBuild ? options.failOnBuild : options.failOnServe) {
|
|
421
|
+
this.error(err);
|
|
422
|
+
} else {
|
|
423
|
+
this.warn(err);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return {
|
|
427
|
+
name: "glob-guard",
|
|
428
|
+
// We must run before Vite's internal TS/JS transforms (esbuild), otherwise
|
|
429
|
+
// comment annotations may be stripped before we can read them.
|
|
430
|
+
enforce: "pre",
|
|
431
|
+
configResolved(config) {
|
|
432
|
+
root = slash(config.root);
|
|
433
|
+
isBuild = config.command === "build";
|
|
434
|
+
},
|
|
435
|
+
transform(code, id) {
|
|
436
|
+
if (matchesAny(id, options.ignoreImporters)) return null;
|
|
437
|
+
let parsed;
|
|
438
|
+
try {
|
|
439
|
+
const file = stripQuery(id);
|
|
440
|
+
if (file.endsWith(".vue") || file.endsWith(".svelte")) {
|
|
441
|
+
parsed = parseRequiredGlobCallsInScriptTags(code, id, { mode: options.mode });
|
|
442
|
+
} else if (file.endsWith(".astro")) {
|
|
443
|
+
parsed = parseRequiredGlobCallsInAstroFrontmatter(code, id, { mode: options.mode });
|
|
444
|
+
} else {
|
|
445
|
+
parsed = parseRequiredGlobCalls(code, id, { mode: options.mode });
|
|
446
|
+
}
|
|
447
|
+
} catch {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
if (parsed.length > 0) callsites.push(...parsed);
|
|
451
|
+
return null;
|
|
452
|
+
},
|
|
453
|
+
async buildEnd() {
|
|
454
|
+
await checkAll.call(this);
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
if (typeof module !== 'undefined' && module.exports && module.exports.default) { module.exports = module.exports.default }
|
|
459
|
+
|
|
460
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/parse.ts","../src/utils.ts","../src/resolve.ts","../src/match.ts","../src/diagnostics.ts"],"sourcesContent":["import type { Plugin } from 'vite'\nimport {\n parseRequiredGlobCalls,\n parseRequiredGlobCallsInAstroFrontmatter,\n parseRequiredGlobCallsInScriptTags,\n type ParsedGlobCallsite,\n} from './parse'\nimport { matchesAny, slash, stripQuery } from './utils'\nimport { toAbsoluteGlob } from './resolve'\nimport { matchGlobs } from './match'\nimport { formatManyFailures, type EmptyGlobFailure } from './diagnostics'\nimport type { GlobGuardOptions } from './types'\n\nconst DEFAULTS: Required<Pick<\n GlobGuardOptions,\n 'mode' | 'failOnBuild' | 'checkOnServe' | 'failOnServe' | 'report' | 'ignoreImporters'\n>> = {\n mode: 'annotated',\n failOnBuild: true,\n checkOnServe: false,\n failOnServe: false,\n report: false,\n ignoreImporters: [],\n}\n\nexport default function globGuard(userOptions: GlobGuardOptions = {}): Plugin {\n const options = { ...DEFAULTS, ...userOptions }\n\n let root = ''\n let isBuild = false\n\n const callsites: ParsedGlobCallsite[] = []\n\n async function checkAll(this: any) {\n if (callsites.length === 0) return\n\n const failures: EmptyGlobFailure[] = []\n\n for (const cs of callsites) {\n const importerFile = stripQuery(cs.importer)\n\n const globsResolved = await Promise.all(\n cs.globs.map((g) =>\n toAbsoluteGlob(g, {\n root,\n importer: slash(importerFile),\n base: cs.options.base,\n resolveId: async (id, importer) => {\n const r = await this.resolve(id, importer)\n return r?.id\n },\n }),\n ),\n )\n\n const match = await matchGlobs({\n globsResolved,\n root,\n importerFile,\n exhaustive: cs.options.exhaustive,\n })\n\n if (match.matches.length === 0) {\n failures.push({\n importer: cs.importer,\n importerFile,\n loc: cs.loc,\n globs: cs.globs,\n globsResolved,\n exhaustive: !!cs.options.exhaustive,\n base: cs.options.base,\n cwd: match.cwd,\n ignore: match.ignore,\n })\n }\n }\n\n if (failures.length === 0) return\n\n // Report (optional)\n if (options.report) {\n const reportFile =\n options.report === true\n ? 'glob-guard.report.json'\n : options.report.file\n const fs = await import('node:fs/promises')\n const payload = {\n root,\n failures: failures.map((f) => ({\n importer: slash(f.importerFile),\n loc: f.loc,\n globs: f.globs,\n globsResolved: f.globsResolved,\n exhaustive: f.exhaustive,\n base: f.base,\n cwd: f.cwd,\n ignore: f.ignore,\n })),\n }\n await fs.writeFile(reportFile, JSON.stringify(payload, null, 2), 'utf8')\n }\n\n const err = formatManyFailures(failures)\n if (isBuild ? options.failOnBuild : options.failOnServe) {\n this.error(err)\n } else {\n this.warn(err)\n }\n }\n\n return {\n name: 'glob-guard',\n\n // We must run before Vite's internal TS/JS transforms (esbuild), otherwise\n // comment annotations may be stripped before we can read them.\n enforce: 'pre',\n\n\n configResolved(config) {\n root = slash(config.root)\n isBuild = config.command === 'build'\n },\n\n transform(code, id) {\n if (matchesAny(id, options.ignoreImporters)) return null\n\n let parsed: ParsedGlobCallsite[]\n try {\n const file = stripQuery(id)\n if (file.endsWith('.vue') || file.endsWith('.svelte')) {\n parsed = parseRequiredGlobCallsInScriptTags(code, id, { mode: options.mode })\n } else if (file.endsWith('.astro')) {\n parsed = parseRequiredGlobCallsInAstroFrontmatter(code, id, { mode: options.mode })\n } else {\n parsed = parseRequiredGlobCalls(code, id, { mode: options.mode })\n }\n } catch {\n // Some files (e.g. Vue/Svelte SFC source) are not valid JS/TS before\n // their framework plugin runs. We intentionally ignore parse failures\n // and only act on call sites we can confidently extract.\n return null\n }\n if (parsed.length > 0) callsites.push(...parsed)\n return null\n },\n\n async buildEnd() {\n await checkAll.call(this)\n },\n }\n}\n","import { parse } from '@babel/parser'\nimport traverse from '@babel/traverse'\nimport type { Comment, Node } from '@babel/types'\n\nexport interface ParsedGlobCallsite {\n importer: string\n globs: string[]\n options: {\n exhaustive?: boolean\n base?: string\n }\n loc: { line: number; column: number } | null\n}\n\nexport interface ParseOptions {\n mode: 'annotated' | 'all'\n}\n\nconst REQUIRED_TOKEN = 'glob-guard:required'\n\nfunction hasRequiredAnnotation(comments: Comment[] | null | undefined): boolean {\n if (!comments || comments.length === 0) return false\n return comments.some((c) => c.value.includes(REQUIRED_TOKEN))\n}\n\nfunction isImportMetaGlobCallee(node: Node): boolean {\n if (node.type !== 'MemberExpression') return false\n if (node.computed) return false\n\n const object = node.object\n const property = node.property\n\n if (object.type !== 'MetaProperty') return false\n if (object.meta.type !== 'Identifier' || object.meta.name !== 'import') return false\n if (object.property.type !== 'Identifier' || object.property.name !== 'meta')\n return false\n\n return property.type === 'Identifier' && property.name === 'glob'\n}\n\nfunction readStringLiteral(node: Node): string | null {\n if (node.type === 'StringLiteral') return node.value\n if (node.type === 'TemplateLiteral') {\n if (node.expressions.length !== 0) return null\n return node.quasis[0]?.value?.raw ?? ''\n }\n return null\n}\n\nfunction readGlobsArg(node: Node): string[] | null {\n const single = readStringLiteral(node)\n if (single != null) return [single]\n if (node.type !== 'ArrayExpression') return null\n\n const globs: string[] = []\n for (const el of node.elements) {\n if (!el) continue\n if (el.type === 'SpreadElement') return null\n const s = readStringLiteral(el)\n if (s == null) return null\n globs.push(s)\n }\n return globs\n}\n\nfunction readOptions(node: Node | null | undefined): {\n exhaustive?: boolean\n base?: string\n} {\n if (!node) return {}\n if (node.type !== 'ObjectExpression') return {}\n\n const out: { exhaustive?: boolean; base?: string } = {}\n for (const prop of node.properties) {\n if (prop.type !== 'ObjectProperty') continue\n if (prop.computed) continue\n if (prop.key.type !== 'Identifier' && prop.key.type !== 'StringLiteral') continue\n\n const key = prop.key.type === 'Identifier' ? prop.key.name : prop.key.value\n if (key === 'exhaustive' && prop.value.type === 'BooleanLiteral') {\n out.exhaustive = prop.value.value\n }\n if (key === 'base') {\n const base = readStringLiteral(prop.value)\n if (base != null) out.base = base\n }\n }\n return out\n}\n\nexport function parseRequiredGlobCalls(\n code: string,\n importer: string,\n options: ParseOptions,\n): ParsedGlobCallsite[] {\n // Fast exits.\n if (!code.includes('import.meta.glob')) return []\n if (options.mode === 'annotated' && !code.includes(REQUIRED_TOKEN)) return []\n\n const ast = parse(code, {\n sourceType: 'module',\n sourceFilename: importer,\n plugins: ['typescript', 'jsx'],\n errorRecovery: true,\n ranges: true,\n attachComment: true,\n })\n\n const results: ParsedGlobCallsite[] = []\n\n traverse(ast, {\n CallExpression(path) {\n if (!isImportMetaGlobCallee(path.node.callee as unknown as Node)) return\n\n const args = path.node.arguments\n if (args.length < 1) return\n const arg0 = args[0]\n const arg1 = args[1]\n\n // @babel/parser uses different node types inside CallExpression.arguments.\n // We rely on runtime checks only.\n const globNode = arg0 as unknown as Node\n const optsNode = (arg1 as unknown as Node | undefined) ?? undefined\n\n const globs = readGlobsArg(globNode)\n if (!globs || globs.length === 0) return\n\n const required =\n options.mode === 'all' ||\n (() => {\n const stmt = path.getStatementParent()?.node as any\n if (!stmt) return false\n return (\n hasRequiredAnnotation(stmt.leadingComments) ||\n hasRequiredAnnotation(stmt.trailingComments) ||\n hasRequiredAnnotation((path.node as any).leadingComments) ||\n hasRequiredAnnotation((path.node as any).trailingComments)\n )\n })()\n\n if (!required) return\n\n const opts = readOptions(optsNode)\n\n results.push({\n importer,\n globs,\n options: opts,\n loc: path.node.loc\n ? { line: path.node.loc.start.line, column: path.node.loc.start.column }\n : null,\n })\n },\n })\n\n return results\n}\n\nfunction getLineAndColumn(source: string, index: number): { line: number; column: number } {\n const prefix = source.slice(0, index)\n const lastNewline = prefix.lastIndexOf('\\n')\n const line = prefix.split('\\n').length\n const column = lastNewline === -1 ? prefix.length : prefix.length - lastNewline - 1\n return { line, column }\n}\n\nfunction offsetLoc(\n loc: { line: number; column: number } | null,\n startLine: number,\n startColumn: number,\n): { line: number; column: number } | null {\n if (!loc) return null\n const line = startLine + loc.line - 1\n const column = loc.line === 1 ? startColumn + loc.column : loc.column\n return { line, column }\n}\n\n/**\n * Extract required glob call sites from <script> blocks inside SFC-ish files\n * (Vue/Svelte). This is a lightweight extractor: it doesn't try to fully parse\n * HTML/SFC syntax; it only slices out script contents.\n */\nexport function parseRequiredGlobCallsInScriptTags(\n code: string,\n importer: string,\n options: ParseOptions,\n): ParsedGlobCallsite[] {\n // If the file can't possibly contain our targets, skip quickly.\n if (!code.includes('<script')) return []\n if (!code.includes('import.meta.glob')) return []\n if (options.mode === 'annotated' && !code.includes(REQUIRED_TOKEN)) return []\n\n const results: ParsedGlobCallsite[] = []\n\n let cursor = 0\n while (cursor < code.length) {\n const open = code.indexOf('<script', cursor)\n if (open === -1) break\n\n const openEnd = code.indexOf('>', open)\n if (openEnd === -1) break\n\n const close = code.indexOf('</script>', openEnd + 1)\n if (close === -1) break\n\n const contentStart = openEnd + 1\n const contentEnd = close\n const content = code.slice(contentStart, contentEnd)\n\n const start = getLineAndColumn(code, contentStart)\n const callsites = parseRequiredGlobCalls(content, importer, options)\n for (const cs of callsites) {\n results.push({\n ...cs,\n loc: offsetLoc(cs.loc, start.line, start.column),\n })\n }\n\n cursor = close + '</script>'.length\n }\n\n return results\n}\n\n/**\n * Extract required glob call sites from Astro frontmatter:\n *\n * ---\n * // TS/JS\n * ---\n */\nexport function parseRequiredGlobCallsInAstroFrontmatter(\n code: string,\n importer: string,\n options: ParseOptions,\n): ParsedGlobCallsite[] {\n if (!code.startsWith('---')) return []\n if (!code.includes('import.meta.glob')) return []\n if (options.mode === 'annotated' && !code.includes(REQUIRED_TOKEN)) return []\n\n const openNewline = code.indexOf('\\n')\n if (openNewline === -1) return []\n const contentStart = openNewline + 1\n\n const fmMatch = code.match(/^---\\s*\\r?\\n([\\s\\S]*?)\\r?\\n---\\s*(?:\\r?\\n|$)/)\n if (!fmMatch) return []\n const frontmatter = fmMatch[1] ?? ''\n\n const start = getLineAndColumn(code, contentStart)\n const callsites = parseRequiredGlobCalls(frontmatter, importer, options)\n return callsites.map((cs) => ({\n ...cs,\n loc: offsetLoc(cs.loc, start.line, start.column),\n }))\n}\n","export function slash(p: string): string {\n return p.replace(/\\\\/g, '/')\n}\n\nexport function stripQuery(id: string): string {\n const i = id.indexOf('?')\n return i === -1 ? id : id.slice(0, i)\n}\n\nexport function toArray<T>(value: T | T[]): T[] {\n return Array.isArray(value) ? value : [value]\n}\n\nexport function matchesAny(\n value: string,\n patterns: (string | RegExp)[] | undefined,\n): boolean {\n if (!patterns || patterns.length === 0) return false\n return patterns.some((p) => (typeof p === 'string' ? p === value : p.test(value)))\n}\n","// Portions of this file are based on Vite's importMetaGlob implementation:\n// https://github.com/vitejs/vite/blob/v7.3.1/packages/vite/src/node/plugins/importMetaGlob.ts\n// Vite is licensed under the MIT License.\n\nimport { isAbsolute, posix } from 'node:path'\nimport picomatch from 'picomatch'\nimport { escapePath } from 'tinyglobby'\nimport { slash } from './utils'\n\nexport interface ResolveContext {\n root: string\n importer: string\n base?: string\n resolveId: (id: string, importer?: string) => Promise<string | undefined>\n}\n\nfunction globSafePath(p: string): string {\n // slash path to ensure \\ is converted to / as \\ could lead to a double escape scenario\n return escapePath(slash(p))\n}\n\nfunction lastNthChar(str: string, n: number) {\n return str.charAt(str.length - 1 - n)\n}\n\nfunction globSafeResolvedPath(resolved: string, glob: string) {\n let numEqual = 0\n const maxEqual = Math.min(resolved.length, glob.length)\n while (numEqual < maxEqual && lastNthChar(resolved, numEqual) === lastNthChar(glob, numEqual)) {\n numEqual += 1\n }\n const staticPartEnd = resolved.length - numEqual\n const staticPart = resolved.slice(0, staticPartEnd)\n const dynamicPart = resolved.slice(staticPartEnd)\n return globSafePath(staticPart) + dynamicPart\n}\n\nexport async function toAbsoluteGlob(glob: string, ctx: ResolveContext): Promise<string> {\n let pre = ''\n if (glob[0] === '!') {\n pre = '!'\n glob = glob.slice(1)\n }\n\n const root = globSafePath(ctx.root)\n\n let dir: string\n if (ctx.base) {\n if (ctx.base[0] === '/') {\n dir = posix.join(root, ctx.base)\n } else {\n dir = posix.resolve(globSafePath(posix.dirname(ctx.importer)), ctx.base)\n }\n } else {\n dir = globSafePath(posix.dirname(ctx.importer))\n }\n\n if (glob[0] === '/') return pre + posix.join(root, glob.slice(1))\n if (glob.startsWith('./')) return pre + posix.join(dir, glob.slice(2))\n if (glob.startsWith('../')) return pre + posix.join(dir, glob)\n if (glob.startsWith('**')) return pre + glob\n\n const resolved = slash((await ctx.resolveId(glob, ctx.importer)) || glob)\n if (isAbsolute(resolved)) {\n return pre + globSafeResolvedPath(resolved, glob)\n }\n\n throw new Error(\n `Invalid glob: \"${glob}\" (resolved: \"${resolved}\"). It must start with '/' or './'`,\n )\n}\n\nexport function getCommonBase(globsResolved: string[]): null | string {\n const bases = globsResolved\n .filter((g) => g[0] !== '!')\n .map((glob) => {\n let { base } = picomatch.scan(glob)\n if (posix.basename(base).includes('.')) base = posix.dirname(base)\n return base\n })\n\n if (!bases.length) return null\n\n let commonAncestor = ''\n const parts = bases[0].split('/')\n for (let i = 0; i < parts.length; i++) {\n const candidate = parts.slice(0, i + 1).join('/')\n if (bases.every((b) => b.startsWith(candidate))) commonAncestor = candidate\n else break\n }\n if (!commonAncestor) commonAncestor = '/'\n return commonAncestor\n}\n","import { glob as tinyGlob } from 'tinyglobby'\nimport { getCommonBase } from './resolve'\nimport { slash } from './utils'\n\nexport interface MatchInput {\n globsResolved: string[]\n root: string\n importerFile: string\n exhaustive?: boolean\n}\n\nexport interface MatchResult {\n cwd: string\n dot: boolean\n ignore: string[]\n matches: string[]\n}\n\nexport async function matchGlobs(input: MatchInput): Promise<MatchResult> {\n const globsResolved = input.globsResolved.map(slash)\n const root = slash(input.root)\n const importerFile = slash(input.importerFile)\n const cwd = getCommonBase(globsResolved) ?? root\n const dot = !!input.exhaustive\n\n const ignore = dot ? [] : ['**/node_modules/**']\n const matches = (\n await tinyGlob(globsResolved, {\n cwd,\n absolute: true,\n dot,\n expandDirectories: false,\n ignore,\n })\n )\n .map(slash)\n .filter((f) => f !== importerFile)\n .sort()\n\n return {\n cwd,\n dot,\n ignore,\n matches,\n }\n}\n","import type { RollupError } from 'rollup'\nimport { slash } from './utils'\n\nexport interface EmptyGlobFailure {\n importer: string\n importerFile: string\n loc: { line: number; column: number } | null\n globs: string[]\n globsResolved: string[]\n exhaustive: boolean\n base?: string\n cwd: string\n ignore: string[]\n}\n\nexport function formatEmptyGlobFailure(f: EmptyGlobFailure): RollupError {\n const importerLabel =\n f.loc != null\n ? `${slash(f.importerFile)}:${f.loc.line}:${f.loc.column + 1}`\n : slash(f.importerFile)\n\n const lines: string[] = []\n lines.push('[glob-guard] Required glob matched 0 files')\n lines.push(` importer: ${importerLabel}`)\n if (f.globs.length === 1) {\n lines.push(` glob: ${f.globs[0]}`)\n } else {\n lines.push(' globs:')\n for (const g of f.globs) lines.push(` - ${g}`)\n }\n lines.push(' resolved:')\n for (const g of f.globsResolved) lines.push(` - ${g}`)\n lines.push(` options: exhaustive=${String(f.exhaustive)}${f.base ? ` base=${f.base}` : ''}`)\n lines.push(` cwd: ${f.cwd}`)\n if (f.ignore.length > 0) {\n lines.push(' ignore:')\n for (const i of f.ignore) lines.push(` - ${i}`)\n }\n lines.push(' hints:')\n lines.push(' - Did you rename/move the directory?')\n lines.push(' - Did you change file extensions?')\n lines.push(' - Are you excluding everything via a negated pattern (\"!...\")?')\n\n const err = new Error(lines.join('\\n')) as RollupError\n err.id = slash(f.importerFile)\n if (f.loc) err.loc = { file: err.id, line: f.loc.line, column: f.loc.column }\n return err\n}\n\nexport function formatManyFailures(failures: EmptyGlobFailure[]): RollupError {\n if (failures.length === 1) return formatEmptyGlobFailure(failures[0])\n\n const lines: string[] = []\n lines.push(`[glob-guard] ${failures.length} required globs matched 0 files`)\n for (const f of failures) {\n const importerLabel =\n f.loc != null\n ? `${slash(f.importerFile)}:${f.loc.line}:${f.loc.column + 1}`\n : slash(f.importerFile)\n lines.push(` - ${importerLabel}`)\n if (f.globs.length === 1) {\n lines.push(` glob: ${f.globs[0]}`)\n } else {\n lines.push(' globs:')\n for (const g of f.globs) lines.push(` - ${g}`)\n }\n }\n const err = new Error(lines.join('\\n')) as RollupError\n return err\n}\n"],"mappings":"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","names":["traverse","picomatch","import_tinyglobby","tinyGlob"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
interface GlobGuardOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Which call sites should be enforced.
|
|
6
|
+
* - 'annotated': only those with a glob-guard:required annotation
|
|
7
|
+
* - 'all': every import.meta.glob call must match at least 1 file
|
|
8
|
+
*/
|
|
9
|
+
mode?: 'annotated' | 'all';
|
|
10
|
+
/** Fail the build on required empty globs. Default: true */
|
|
11
|
+
failOnBuild?: boolean;
|
|
12
|
+
/** Warn (or fail) during dev server. Default: false */
|
|
13
|
+
checkOnServe?: boolean;
|
|
14
|
+
/** If true and checkOnServe enabled, fail dev server start. Default: false */
|
|
15
|
+
failOnServe?: boolean;
|
|
16
|
+
/** Optional JSON report of resolved globs and matches */
|
|
17
|
+
report?: false | true | {
|
|
18
|
+
file: string;
|
|
19
|
+
};
|
|
20
|
+
/** Ignore importer ids (e.g. virtual modules). Optional */
|
|
21
|
+
ignoreImporters?: (string | RegExp)[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare function globGuard(userOptions?: GlobGuardOptions): Plugin;
|
|
25
|
+
|
|
26
|
+
export = globGuard;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
interface GlobGuardOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Which call sites should be enforced.
|
|
6
|
+
* - 'annotated': only those with a glob-guard:required annotation
|
|
7
|
+
* - 'all': every import.meta.glob call must match at least 1 file
|
|
8
|
+
*/
|
|
9
|
+
mode?: 'annotated' | 'all';
|
|
10
|
+
/** Fail the build on required empty globs. Default: true */
|
|
11
|
+
failOnBuild?: boolean;
|
|
12
|
+
/** Warn (or fail) during dev server. Default: false */
|
|
13
|
+
checkOnServe?: boolean;
|
|
14
|
+
/** If true and checkOnServe enabled, fail dev server start. Default: false */
|
|
15
|
+
failOnServe?: boolean;
|
|
16
|
+
/** Optional JSON report of resolved globs and matches */
|
|
17
|
+
report?: false | true | {
|
|
18
|
+
file: string;
|
|
19
|
+
};
|
|
20
|
+
/** Ignore importer ids (e.g. virtual modules). Optional */
|
|
21
|
+
ignoreImporters?: (string | RegExp)[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare function globGuard(userOptions?: GlobGuardOptions): Plugin;
|
|
25
|
+
|
|
26
|
+
export { globGuard as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
// src/parse.ts
|
|
2
|
+
import { parse } from "@babel/parser";
|
|
3
|
+
import traverse from "@babel/traverse";
|
|
4
|
+
var REQUIRED_TOKEN = "glob-guard:required";
|
|
5
|
+
function hasRequiredAnnotation(comments) {
|
|
6
|
+
if (!comments || comments.length === 0) return false;
|
|
7
|
+
return comments.some((c) => c.value.includes(REQUIRED_TOKEN));
|
|
8
|
+
}
|
|
9
|
+
function isImportMetaGlobCallee(node) {
|
|
10
|
+
if (node.type !== "MemberExpression") return false;
|
|
11
|
+
if (node.computed) return false;
|
|
12
|
+
const object = node.object;
|
|
13
|
+
const property = node.property;
|
|
14
|
+
if (object.type !== "MetaProperty") return false;
|
|
15
|
+
if (object.meta.type !== "Identifier" || object.meta.name !== "import") return false;
|
|
16
|
+
if (object.property.type !== "Identifier" || object.property.name !== "meta")
|
|
17
|
+
return false;
|
|
18
|
+
return property.type === "Identifier" && property.name === "glob";
|
|
19
|
+
}
|
|
20
|
+
function readStringLiteral(node) {
|
|
21
|
+
if (node.type === "StringLiteral") return node.value;
|
|
22
|
+
if (node.type === "TemplateLiteral") {
|
|
23
|
+
if (node.expressions.length !== 0) return null;
|
|
24
|
+
return node.quasis[0]?.value?.raw ?? "";
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
function readGlobsArg(node) {
|
|
29
|
+
const single = readStringLiteral(node);
|
|
30
|
+
if (single != null) return [single];
|
|
31
|
+
if (node.type !== "ArrayExpression") return null;
|
|
32
|
+
const globs = [];
|
|
33
|
+
for (const el of node.elements) {
|
|
34
|
+
if (!el) continue;
|
|
35
|
+
if (el.type === "SpreadElement") return null;
|
|
36
|
+
const s = readStringLiteral(el);
|
|
37
|
+
if (s == null) return null;
|
|
38
|
+
globs.push(s);
|
|
39
|
+
}
|
|
40
|
+
return globs;
|
|
41
|
+
}
|
|
42
|
+
function readOptions(node) {
|
|
43
|
+
if (!node) return {};
|
|
44
|
+
if (node.type !== "ObjectExpression") return {};
|
|
45
|
+
const out = {};
|
|
46
|
+
for (const prop of node.properties) {
|
|
47
|
+
if (prop.type !== "ObjectProperty") continue;
|
|
48
|
+
if (prop.computed) continue;
|
|
49
|
+
if (prop.key.type !== "Identifier" && prop.key.type !== "StringLiteral") continue;
|
|
50
|
+
const key = prop.key.type === "Identifier" ? prop.key.name : prop.key.value;
|
|
51
|
+
if (key === "exhaustive" && prop.value.type === "BooleanLiteral") {
|
|
52
|
+
out.exhaustive = prop.value.value;
|
|
53
|
+
}
|
|
54
|
+
if (key === "base") {
|
|
55
|
+
const base = readStringLiteral(prop.value);
|
|
56
|
+
if (base != null) out.base = base;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
function parseRequiredGlobCalls(code, importer, options) {
|
|
62
|
+
if (!code.includes("import.meta.glob")) return [];
|
|
63
|
+
if (options.mode === "annotated" && !code.includes(REQUIRED_TOKEN)) return [];
|
|
64
|
+
const ast = parse(code, {
|
|
65
|
+
sourceType: "module",
|
|
66
|
+
sourceFilename: importer,
|
|
67
|
+
plugins: ["typescript", "jsx"],
|
|
68
|
+
errorRecovery: true,
|
|
69
|
+
ranges: true,
|
|
70
|
+
attachComment: true
|
|
71
|
+
});
|
|
72
|
+
const results = [];
|
|
73
|
+
traverse(ast, {
|
|
74
|
+
CallExpression(path) {
|
|
75
|
+
if (!isImportMetaGlobCallee(path.node.callee)) return;
|
|
76
|
+
const args = path.node.arguments;
|
|
77
|
+
if (args.length < 1) return;
|
|
78
|
+
const arg0 = args[0];
|
|
79
|
+
const arg1 = args[1];
|
|
80
|
+
const globNode = arg0;
|
|
81
|
+
const optsNode = arg1 ?? void 0;
|
|
82
|
+
const globs = readGlobsArg(globNode);
|
|
83
|
+
if (!globs || globs.length === 0) return;
|
|
84
|
+
const required = options.mode === "all" || (() => {
|
|
85
|
+
const stmt = path.getStatementParent()?.node;
|
|
86
|
+
if (!stmt) return false;
|
|
87
|
+
return hasRequiredAnnotation(stmt.leadingComments) || hasRequiredAnnotation(stmt.trailingComments) || hasRequiredAnnotation(path.node.leadingComments) || hasRequiredAnnotation(path.node.trailingComments);
|
|
88
|
+
})();
|
|
89
|
+
if (!required) return;
|
|
90
|
+
const opts = readOptions(optsNode);
|
|
91
|
+
results.push({
|
|
92
|
+
importer,
|
|
93
|
+
globs,
|
|
94
|
+
options: opts,
|
|
95
|
+
loc: path.node.loc ? { line: path.node.loc.start.line, column: path.node.loc.start.column } : null
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
return results;
|
|
100
|
+
}
|
|
101
|
+
function getLineAndColumn(source, index) {
|
|
102
|
+
const prefix = source.slice(0, index);
|
|
103
|
+
const lastNewline = prefix.lastIndexOf("\n");
|
|
104
|
+
const line = prefix.split("\n").length;
|
|
105
|
+
const column = lastNewline === -1 ? prefix.length : prefix.length - lastNewline - 1;
|
|
106
|
+
return { line, column };
|
|
107
|
+
}
|
|
108
|
+
function offsetLoc(loc, startLine, startColumn) {
|
|
109
|
+
if (!loc) return null;
|
|
110
|
+
const line = startLine + loc.line - 1;
|
|
111
|
+
const column = loc.line === 1 ? startColumn + loc.column : loc.column;
|
|
112
|
+
return { line, column };
|
|
113
|
+
}
|
|
114
|
+
function parseRequiredGlobCallsInScriptTags(code, importer, options) {
|
|
115
|
+
if (!code.includes("<script")) return [];
|
|
116
|
+
if (!code.includes("import.meta.glob")) return [];
|
|
117
|
+
if (options.mode === "annotated" && !code.includes(REQUIRED_TOKEN)) return [];
|
|
118
|
+
const results = [];
|
|
119
|
+
let cursor = 0;
|
|
120
|
+
while (cursor < code.length) {
|
|
121
|
+
const open = code.indexOf("<script", cursor);
|
|
122
|
+
if (open === -1) break;
|
|
123
|
+
const openEnd = code.indexOf(">", open);
|
|
124
|
+
if (openEnd === -1) break;
|
|
125
|
+
const close = code.indexOf("</script>", openEnd + 1);
|
|
126
|
+
if (close === -1) break;
|
|
127
|
+
const contentStart = openEnd + 1;
|
|
128
|
+
const contentEnd = close;
|
|
129
|
+
const content = code.slice(contentStart, contentEnd);
|
|
130
|
+
const start = getLineAndColumn(code, contentStart);
|
|
131
|
+
const callsites = parseRequiredGlobCalls(content, importer, options);
|
|
132
|
+
for (const cs of callsites) {
|
|
133
|
+
results.push({
|
|
134
|
+
...cs,
|
|
135
|
+
loc: offsetLoc(cs.loc, start.line, start.column)
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
cursor = close + "</script>".length;
|
|
139
|
+
}
|
|
140
|
+
return results;
|
|
141
|
+
}
|
|
142
|
+
function parseRequiredGlobCallsInAstroFrontmatter(code, importer, options) {
|
|
143
|
+
if (!code.startsWith("---")) return [];
|
|
144
|
+
if (!code.includes("import.meta.glob")) return [];
|
|
145
|
+
if (options.mode === "annotated" && !code.includes(REQUIRED_TOKEN)) return [];
|
|
146
|
+
const openNewline = code.indexOf("\n");
|
|
147
|
+
if (openNewline === -1) return [];
|
|
148
|
+
const contentStart = openNewline + 1;
|
|
149
|
+
const fmMatch = code.match(/^---\s*\r?\n([\s\S]*?)\r?\n---\s*(?:\r?\n|$)/);
|
|
150
|
+
if (!fmMatch) return [];
|
|
151
|
+
const frontmatter = fmMatch[1] ?? "";
|
|
152
|
+
const start = getLineAndColumn(code, contentStart);
|
|
153
|
+
const callsites = parseRequiredGlobCalls(frontmatter, importer, options);
|
|
154
|
+
return callsites.map((cs) => ({
|
|
155
|
+
...cs,
|
|
156
|
+
loc: offsetLoc(cs.loc, start.line, start.column)
|
|
157
|
+
}));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// src/utils.ts
|
|
161
|
+
function slash(p) {
|
|
162
|
+
return p.replace(/\\/g, "/");
|
|
163
|
+
}
|
|
164
|
+
function stripQuery(id) {
|
|
165
|
+
const i = id.indexOf("?");
|
|
166
|
+
return i === -1 ? id : id.slice(0, i);
|
|
167
|
+
}
|
|
168
|
+
function matchesAny(value, patterns) {
|
|
169
|
+
if (!patterns || patterns.length === 0) return false;
|
|
170
|
+
return patterns.some((p) => typeof p === "string" ? p === value : p.test(value));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/resolve.ts
|
|
174
|
+
import { isAbsolute, posix } from "path";
|
|
175
|
+
import picomatch from "picomatch";
|
|
176
|
+
import { escapePath } from "tinyglobby";
|
|
177
|
+
function globSafePath(p) {
|
|
178
|
+
return escapePath(slash(p));
|
|
179
|
+
}
|
|
180
|
+
function lastNthChar(str, n) {
|
|
181
|
+
return str.charAt(str.length - 1 - n);
|
|
182
|
+
}
|
|
183
|
+
function globSafeResolvedPath(resolved, glob) {
|
|
184
|
+
let numEqual = 0;
|
|
185
|
+
const maxEqual = Math.min(resolved.length, glob.length);
|
|
186
|
+
while (numEqual < maxEqual && lastNthChar(resolved, numEqual) === lastNthChar(glob, numEqual)) {
|
|
187
|
+
numEqual += 1;
|
|
188
|
+
}
|
|
189
|
+
const staticPartEnd = resolved.length - numEqual;
|
|
190
|
+
const staticPart = resolved.slice(0, staticPartEnd);
|
|
191
|
+
const dynamicPart = resolved.slice(staticPartEnd);
|
|
192
|
+
return globSafePath(staticPart) + dynamicPart;
|
|
193
|
+
}
|
|
194
|
+
async function toAbsoluteGlob(glob, ctx) {
|
|
195
|
+
let pre = "";
|
|
196
|
+
if (glob[0] === "!") {
|
|
197
|
+
pre = "!";
|
|
198
|
+
glob = glob.slice(1);
|
|
199
|
+
}
|
|
200
|
+
const root = globSafePath(ctx.root);
|
|
201
|
+
let dir;
|
|
202
|
+
if (ctx.base) {
|
|
203
|
+
if (ctx.base[0] === "/") {
|
|
204
|
+
dir = posix.join(root, ctx.base);
|
|
205
|
+
} else {
|
|
206
|
+
dir = posix.resolve(globSafePath(posix.dirname(ctx.importer)), ctx.base);
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
dir = globSafePath(posix.dirname(ctx.importer));
|
|
210
|
+
}
|
|
211
|
+
if (glob[0] === "/") return pre + posix.join(root, glob.slice(1));
|
|
212
|
+
if (glob.startsWith("./")) return pre + posix.join(dir, glob.slice(2));
|
|
213
|
+
if (glob.startsWith("../")) return pre + posix.join(dir, glob);
|
|
214
|
+
if (glob.startsWith("**")) return pre + glob;
|
|
215
|
+
const resolved = slash(await ctx.resolveId(glob, ctx.importer) || glob);
|
|
216
|
+
if (isAbsolute(resolved)) {
|
|
217
|
+
return pre + globSafeResolvedPath(resolved, glob);
|
|
218
|
+
}
|
|
219
|
+
throw new Error(
|
|
220
|
+
`Invalid glob: "${glob}" (resolved: "${resolved}"). It must start with '/' or './'`
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
function getCommonBase(globsResolved) {
|
|
224
|
+
const bases = globsResolved.filter((g) => g[0] !== "!").map((glob) => {
|
|
225
|
+
let { base } = picomatch.scan(glob);
|
|
226
|
+
if (posix.basename(base).includes(".")) base = posix.dirname(base);
|
|
227
|
+
return base;
|
|
228
|
+
});
|
|
229
|
+
if (!bases.length) return null;
|
|
230
|
+
let commonAncestor = "";
|
|
231
|
+
const parts = bases[0].split("/");
|
|
232
|
+
for (let i = 0; i < parts.length; i++) {
|
|
233
|
+
const candidate = parts.slice(0, i + 1).join("/");
|
|
234
|
+
if (bases.every((b) => b.startsWith(candidate))) commonAncestor = candidate;
|
|
235
|
+
else break;
|
|
236
|
+
}
|
|
237
|
+
if (!commonAncestor) commonAncestor = "/";
|
|
238
|
+
return commonAncestor;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/match.ts
|
|
242
|
+
import { glob as tinyGlob } from "tinyglobby";
|
|
243
|
+
async function matchGlobs(input) {
|
|
244
|
+
const globsResolved = input.globsResolved.map(slash);
|
|
245
|
+
const root = slash(input.root);
|
|
246
|
+
const importerFile = slash(input.importerFile);
|
|
247
|
+
const cwd = getCommonBase(globsResolved) ?? root;
|
|
248
|
+
const dot = !!input.exhaustive;
|
|
249
|
+
const ignore = dot ? [] : ["**/node_modules/**"];
|
|
250
|
+
const matches = (await tinyGlob(globsResolved, {
|
|
251
|
+
cwd,
|
|
252
|
+
absolute: true,
|
|
253
|
+
dot,
|
|
254
|
+
expandDirectories: false,
|
|
255
|
+
ignore
|
|
256
|
+
})).map(slash).filter((f) => f !== importerFile).sort();
|
|
257
|
+
return {
|
|
258
|
+
cwd,
|
|
259
|
+
dot,
|
|
260
|
+
ignore,
|
|
261
|
+
matches
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// src/diagnostics.ts
|
|
266
|
+
function formatEmptyGlobFailure(f) {
|
|
267
|
+
const importerLabel = f.loc != null ? `${slash(f.importerFile)}:${f.loc.line}:${f.loc.column + 1}` : slash(f.importerFile);
|
|
268
|
+
const lines = [];
|
|
269
|
+
lines.push("[glob-guard] Required glob matched 0 files");
|
|
270
|
+
lines.push(` importer: ${importerLabel}`);
|
|
271
|
+
if (f.globs.length === 1) {
|
|
272
|
+
lines.push(` glob: ${f.globs[0]}`);
|
|
273
|
+
} else {
|
|
274
|
+
lines.push(" globs:");
|
|
275
|
+
for (const g of f.globs) lines.push(` - ${g}`);
|
|
276
|
+
}
|
|
277
|
+
lines.push(" resolved:");
|
|
278
|
+
for (const g of f.globsResolved) lines.push(` - ${g}`);
|
|
279
|
+
lines.push(` options: exhaustive=${String(f.exhaustive)}${f.base ? ` base=${f.base}` : ""}`);
|
|
280
|
+
lines.push(` cwd: ${f.cwd}`);
|
|
281
|
+
if (f.ignore.length > 0) {
|
|
282
|
+
lines.push(" ignore:");
|
|
283
|
+
for (const i of f.ignore) lines.push(` - ${i}`);
|
|
284
|
+
}
|
|
285
|
+
lines.push(" hints:");
|
|
286
|
+
lines.push(" - Did you rename/move the directory?");
|
|
287
|
+
lines.push(" - Did you change file extensions?");
|
|
288
|
+
lines.push(' - Are you excluding everything via a negated pattern ("!...")?');
|
|
289
|
+
const err = new Error(lines.join("\n"));
|
|
290
|
+
err.id = slash(f.importerFile);
|
|
291
|
+
if (f.loc) err.loc = { file: err.id, line: f.loc.line, column: f.loc.column };
|
|
292
|
+
return err;
|
|
293
|
+
}
|
|
294
|
+
function formatManyFailures(failures) {
|
|
295
|
+
if (failures.length === 1) return formatEmptyGlobFailure(failures[0]);
|
|
296
|
+
const lines = [];
|
|
297
|
+
lines.push(`[glob-guard] ${failures.length} required globs matched 0 files`);
|
|
298
|
+
for (const f of failures) {
|
|
299
|
+
const importerLabel = f.loc != null ? `${slash(f.importerFile)}:${f.loc.line}:${f.loc.column + 1}` : slash(f.importerFile);
|
|
300
|
+
lines.push(` - ${importerLabel}`);
|
|
301
|
+
if (f.globs.length === 1) {
|
|
302
|
+
lines.push(` glob: ${f.globs[0]}`);
|
|
303
|
+
} else {
|
|
304
|
+
lines.push(" globs:");
|
|
305
|
+
for (const g of f.globs) lines.push(` - ${g}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const err = new Error(lines.join("\n"));
|
|
309
|
+
return err;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// src/index.ts
|
|
313
|
+
var DEFAULTS = {
|
|
314
|
+
mode: "annotated",
|
|
315
|
+
failOnBuild: true,
|
|
316
|
+
checkOnServe: false,
|
|
317
|
+
failOnServe: false,
|
|
318
|
+
report: false,
|
|
319
|
+
ignoreImporters: []
|
|
320
|
+
};
|
|
321
|
+
function globGuard(userOptions = {}) {
|
|
322
|
+
const options = { ...DEFAULTS, ...userOptions };
|
|
323
|
+
let root = "";
|
|
324
|
+
let isBuild = false;
|
|
325
|
+
const callsites = [];
|
|
326
|
+
async function checkAll() {
|
|
327
|
+
if (callsites.length === 0) return;
|
|
328
|
+
const failures = [];
|
|
329
|
+
for (const cs of callsites) {
|
|
330
|
+
const importerFile = stripQuery(cs.importer);
|
|
331
|
+
const globsResolved = await Promise.all(
|
|
332
|
+
cs.globs.map(
|
|
333
|
+
(g) => toAbsoluteGlob(g, {
|
|
334
|
+
root,
|
|
335
|
+
importer: slash(importerFile),
|
|
336
|
+
base: cs.options.base,
|
|
337
|
+
resolveId: async (id, importer) => {
|
|
338
|
+
const r = await this.resolve(id, importer);
|
|
339
|
+
return r?.id;
|
|
340
|
+
}
|
|
341
|
+
})
|
|
342
|
+
)
|
|
343
|
+
);
|
|
344
|
+
const match = await matchGlobs({
|
|
345
|
+
globsResolved,
|
|
346
|
+
root,
|
|
347
|
+
importerFile,
|
|
348
|
+
exhaustive: cs.options.exhaustive
|
|
349
|
+
});
|
|
350
|
+
if (match.matches.length === 0) {
|
|
351
|
+
failures.push({
|
|
352
|
+
importer: cs.importer,
|
|
353
|
+
importerFile,
|
|
354
|
+
loc: cs.loc,
|
|
355
|
+
globs: cs.globs,
|
|
356
|
+
globsResolved,
|
|
357
|
+
exhaustive: !!cs.options.exhaustive,
|
|
358
|
+
base: cs.options.base,
|
|
359
|
+
cwd: match.cwd,
|
|
360
|
+
ignore: match.ignore
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (failures.length === 0) return;
|
|
365
|
+
if (options.report) {
|
|
366
|
+
const reportFile = options.report === true ? "glob-guard.report.json" : options.report.file;
|
|
367
|
+
const fs = await import("fs/promises");
|
|
368
|
+
const payload = {
|
|
369
|
+
root,
|
|
370
|
+
failures: failures.map((f) => ({
|
|
371
|
+
importer: slash(f.importerFile),
|
|
372
|
+
loc: f.loc,
|
|
373
|
+
globs: f.globs,
|
|
374
|
+
globsResolved: f.globsResolved,
|
|
375
|
+
exhaustive: f.exhaustive,
|
|
376
|
+
base: f.base,
|
|
377
|
+
cwd: f.cwd,
|
|
378
|
+
ignore: f.ignore
|
|
379
|
+
}))
|
|
380
|
+
};
|
|
381
|
+
await fs.writeFile(reportFile, JSON.stringify(payload, null, 2), "utf8");
|
|
382
|
+
}
|
|
383
|
+
const err = formatManyFailures(failures);
|
|
384
|
+
if (isBuild ? options.failOnBuild : options.failOnServe) {
|
|
385
|
+
this.error(err);
|
|
386
|
+
} else {
|
|
387
|
+
this.warn(err);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return {
|
|
391
|
+
name: "glob-guard",
|
|
392
|
+
// We must run before Vite's internal TS/JS transforms (esbuild), otherwise
|
|
393
|
+
// comment annotations may be stripped before we can read them.
|
|
394
|
+
enforce: "pre",
|
|
395
|
+
configResolved(config) {
|
|
396
|
+
root = slash(config.root);
|
|
397
|
+
isBuild = config.command === "build";
|
|
398
|
+
},
|
|
399
|
+
transform(code, id) {
|
|
400
|
+
if (matchesAny(id, options.ignoreImporters)) return null;
|
|
401
|
+
let parsed;
|
|
402
|
+
try {
|
|
403
|
+
const file = stripQuery(id);
|
|
404
|
+
if (file.endsWith(".vue") || file.endsWith(".svelte")) {
|
|
405
|
+
parsed = parseRequiredGlobCallsInScriptTags(code, id, { mode: options.mode });
|
|
406
|
+
} else if (file.endsWith(".astro")) {
|
|
407
|
+
parsed = parseRequiredGlobCallsInAstroFrontmatter(code, id, { mode: options.mode });
|
|
408
|
+
} else {
|
|
409
|
+
parsed = parseRequiredGlobCalls(code, id, { mode: options.mode });
|
|
410
|
+
}
|
|
411
|
+
} catch {
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
if (parsed.length > 0) callsites.push(...parsed);
|
|
415
|
+
return null;
|
|
416
|
+
},
|
|
417
|
+
async buildEnd() {
|
|
418
|
+
await checkAll.call(this);
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
export {
|
|
423
|
+
globGuard as default
|
|
424
|
+
};
|
|
425
|
+
if (typeof module !== 'undefined' && module.exports && module.exports.default) { module.exports = module.exports.default }
|
|
426
|
+
|
|
427
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/parse.ts","../src/utils.ts","../src/resolve.ts","../src/match.ts","../src/diagnostics.ts","../src/index.ts"],"sourcesContent":["import { parse } from '@babel/parser'\nimport traverse from '@babel/traverse'\nimport type { Comment, Node } from '@babel/types'\n\nexport interface ParsedGlobCallsite {\n importer: string\n globs: string[]\n options: {\n exhaustive?: boolean\n base?: string\n }\n loc: { line: number; column: number } | null\n}\n\nexport interface ParseOptions {\n mode: 'annotated' | 'all'\n}\n\nconst REQUIRED_TOKEN = 'glob-guard:required'\n\nfunction hasRequiredAnnotation(comments: Comment[] | null | undefined): boolean {\n if (!comments || comments.length === 0) return false\n return comments.some((c) => c.value.includes(REQUIRED_TOKEN))\n}\n\nfunction isImportMetaGlobCallee(node: Node): boolean {\n if (node.type !== 'MemberExpression') return false\n if (node.computed) return false\n\n const object = node.object\n const property = node.property\n\n if (object.type !== 'MetaProperty') return false\n if (object.meta.type !== 'Identifier' || object.meta.name !== 'import') return false\n if (object.property.type !== 'Identifier' || object.property.name !== 'meta')\n return false\n\n return property.type === 'Identifier' && property.name === 'glob'\n}\n\nfunction readStringLiteral(node: Node): string | null {\n if (node.type === 'StringLiteral') return node.value\n if (node.type === 'TemplateLiteral') {\n if (node.expressions.length !== 0) return null\n return node.quasis[0]?.value?.raw ?? ''\n }\n return null\n}\n\nfunction readGlobsArg(node: Node): string[] | null {\n const single = readStringLiteral(node)\n if (single != null) return [single]\n if (node.type !== 'ArrayExpression') return null\n\n const globs: string[] = []\n for (const el of node.elements) {\n if (!el) continue\n if (el.type === 'SpreadElement') return null\n const s = readStringLiteral(el)\n if (s == null) return null\n globs.push(s)\n }\n return globs\n}\n\nfunction readOptions(node: Node | null | undefined): {\n exhaustive?: boolean\n base?: string\n} {\n if (!node) return {}\n if (node.type !== 'ObjectExpression') return {}\n\n const out: { exhaustive?: boolean; base?: string } = {}\n for (const prop of node.properties) {\n if (prop.type !== 'ObjectProperty') continue\n if (prop.computed) continue\n if (prop.key.type !== 'Identifier' && prop.key.type !== 'StringLiteral') continue\n\n const key = prop.key.type === 'Identifier' ? prop.key.name : prop.key.value\n if (key === 'exhaustive' && prop.value.type === 'BooleanLiteral') {\n out.exhaustive = prop.value.value\n }\n if (key === 'base') {\n const base = readStringLiteral(prop.value)\n if (base != null) out.base = base\n }\n }\n return out\n}\n\nexport function parseRequiredGlobCalls(\n code: string,\n importer: string,\n options: ParseOptions,\n): ParsedGlobCallsite[] {\n // Fast exits.\n if (!code.includes('import.meta.glob')) return []\n if (options.mode === 'annotated' && !code.includes(REQUIRED_TOKEN)) return []\n\n const ast = parse(code, {\n sourceType: 'module',\n sourceFilename: importer,\n plugins: ['typescript', 'jsx'],\n errorRecovery: true,\n ranges: true,\n attachComment: true,\n })\n\n const results: ParsedGlobCallsite[] = []\n\n traverse(ast, {\n CallExpression(path) {\n if (!isImportMetaGlobCallee(path.node.callee as unknown as Node)) return\n\n const args = path.node.arguments\n if (args.length < 1) return\n const arg0 = args[0]\n const arg1 = args[1]\n\n // @babel/parser uses different node types inside CallExpression.arguments.\n // We rely on runtime checks only.\n const globNode = arg0 as unknown as Node\n const optsNode = (arg1 as unknown as Node | undefined) ?? undefined\n\n const globs = readGlobsArg(globNode)\n if (!globs || globs.length === 0) return\n\n const required =\n options.mode === 'all' ||\n (() => {\n const stmt = path.getStatementParent()?.node as any\n if (!stmt) return false\n return (\n hasRequiredAnnotation(stmt.leadingComments) ||\n hasRequiredAnnotation(stmt.trailingComments) ||\n hasRequiredAnnotation((path.node as any).leadingComments) ||\n hasRequiredAnnotation((path.node as any).trailingComments)\n )\n })()\n\n if (!required) return\n\n const opts = readOptions(optsNode)\n\n results.push({\n importer,\n globs,\n options: opts,\n loc: path.node.loc\n ? { line: path.node.loc.start.line, column: path.node.loc.start.column }\n : null,\n })\n },\n })\n\n return results\n}\n\nfunction getLineAndColumn(source: string, index: number): { line: number; column: number } {\n const prefix = source.slice(0, index)\n const lastNewline = prefix.lastIndexOf('\\n')\n const line = prefix.split('\\n').length\n const column = lastNewline === -1 ? prefix.length : prefix.length - lastNewline - 1\n return { line, column }\n}\n\nfunction offsetLoc(\n loc: { line: number; column: number } | null,\n startLine: number,\n startColumn: number,\n): { line: number; column: number } | null {\n if (!loc) return null\n const line = startLine + loc.line - 1\n const column = loc.line === 1 ? startColumn + loc.column : loc.column\n return { line, column }\n}\n\n/**\n * Extract required glob call sites from <script> blocks inside SFC-ish files\n * (Vue/Svelte). This is a lightweight extractor: it doesn't try to fully parse\n * HTML/SFC syntax; it only slices out script contents.\n */\nexport function parseRequiredGlobCallsInScriptTags(\n code: string,\n importer: string,\n options: ParseOptions,\n): ParsedGlobCallsite[] {\n // If the file can't possibly contain our targets, skip quickly.\n if (!code.includes('<script')) return []\n if (!code.includes('import.meta.glob')) return []\n if (options.mode === 'annotated' && !code.includes(REQUIRED_TOKEN)) return []\n\n const results: ParsedGlobCallsite[] = []\n\n let cursor = 0\n while (cursor < code.length) {\n const open = code.indexOf('<script', cursor)\n if (open === -1) break\n\n const openEnd = code.indexOf('>', open)\n if (openEnd === -1) break\n\n const close = code.indexOf('</script>', openEnd + 1)\n if (close === -1) break\n\n const contentStart = openEnd + 1\n const contentEnd = close\n const content = code.slice(contentStart, contentEnd)\n\n const start = getLineAndColumn(code, contentStart)\n const callsites = parseRequiredGlobCalls(content, importer, options)\n for (const cs of callsites) {\n results.push({\n ...cs,\n loc: offsetLoc(cs.loc, start.line, start.column),\n })\n }\n\n cursor = close + '</script>'.length\n }\n\n return results\n}\n\n/**\n * Extract required glob call sites from Astro frontmatter:\n *\n * ---\n * // TS/JS\n * ---\n */\nexport function parseRequiredGlobCallsInAstroFrontmatter(\n code: string,\n importer: string,\n options: ParseOptions,\n): ParsedGlobCallsite[] {\n if (!code.startsWith('---')) return []\n if (!code.includes('import.meta.glob')) return []\n if (options.mode === 'annotated' && !code.includes(REQUIRED_TOKEN)) return []\n\n const openNewline = code.indexOf('\\n')\n if (openNewline === -1) return []\n const contentStart = openNewline + 1\n\n const fmMatch = code.match(/^---\\s*\\r?\\n([\\s\\S]*?)\\r?\\n---\\s*(?:\\r?\\n|$)/)\n if (!fmMatch) return []\n const frontmatter = fmMatch[1] ?? ''\n\n const start = getLineAndColumn(code, contentStart)\n const callsites = parseRequiredGlobCalls(frontmatter, importer, options)\n return callsites.map((cs) => ({\n ...cs,\n loc: offsetLoc(cs.loc, start.line, start.column),\n }))\n}\n","export function slash(p: string): string {\n return p.replace(/\\\\/g, '/')\n}\n\nexport function stripQuery(id: string): string {\n const i = id.indexOf('?')\n return i === -1 ? id : id.slice(0, i)\n}\n\nexport function toArray<T>(value: T | T[]): T[] {\n return Array.isArray(value) ? value : [value]\n}\n\nexport function matchesAny(\n value: string,\n patterns: (string | RegExp)[] | undefined,\n): boolean {\n if (!patterns || patterns.length === 0) return false\n return patterns.some((p) => (typeof p === 'string' ? p === value : p.test(value)))\n}\n","// Portions of this file are based on Vite's importMetaGlob implementation:\n// https://github.com/vitejs/vite/blob/v7.3.1/packages/vite/src/node/plugins/importMetaGlob.ts\n// Vite is licensed under the MIT License.\n\nimport { isAbsolute, posix } from 'node:path'\nimport picomatch from 'picomatch'\nimport { escapePath } from 'tinyglobby'\nimport { slash } from './utils'\n\nexport interface ResolveContext {\n root: string\n importer: string\n base?: string\n resolveId: (id: string, importer?: string) => Promise<string | undefined>\n}\n\nfunction globSafePath(p: string): string {\n // slash path to ensure \\ is converted to / as \\ could lead to a double escape scenario\n return escapePath(slash(p))\n}\n\nfunction lastNthChar(str: string, n: number) {\n return str.charAt(str.length - 1 - n)\n}\n\nfunction globSafeResolvedPath(resolved: string, glob: string) {\n let numEqual = 0\n const maxEqual = Math.min(resolved.length, glob.length)\n while (numEqual < maxEqual && lastNthChar(resolved, numEqual) === lastNthChar(glob, numEqual)) {\n numEqual += 1\n }\n const staticPartEnd = resolved.length - numEqual\n const staticPart = resolved.slice(0, staticPartEnd)\n const dynamicPart = resolved.slice(staticPartEnd)\n return globSafePath(staticPart) + dynamicPart\n}\n\nexport async function toAbsoluteGlob(glob: string, ctx: ResolveContext): Promise<string> {\n let pre = ''\n if (glob[0] === '!') {\n pre = '!'\n glob = glob.slice(1)\n }\n\n const root = globSafePath(ctx.root)\n\n let dir: string\n if (ctx.base) {\n if (ctx.base[0] === '/') {\n dir = posix.join(root, ctx.base)\n } else {\n dir = posix.resolve(globSafePath(posix.dirname(ctx.importer)), ctx.base)\n }\n } else {\n dir = globSafePath(posix.dirname(ctx.importer))\n }\n\n if (glob[0] === '/') return pre + posix.join(root, glob.slice(1))\n if (glob.startsWith('./')) return pre + posix.join(dir, glob.slice(2))\n if (glob.startsWith('../')) return pre + posix.join(dir, glob)\n if (glob.startsWith('**')) return pre + glob\n\n const resolved = slash((await ctx.resolveId(glob, ctx.importer)) || glob)\n if (isAbsolute(resolved)) {\n return pre + globSafeResolvedPath(resolved, glob)\n }\n\n throw new Error(\n `Invalid glob: \"${glob}\" (resolved: \"${resolved}\"). It must start with '/' or './'`,\n )\n}\n\nexport function getCommonBase(globsResolved: string[]): null | string {\n const bases = globsResolved\n .filter((g) => g[0] !== '!')\n .map((glob) => {\n let { base } = picomatch.scan(glob)\n if (posix.basename(base).includes('.')) base = posix.dirname(base)\n return base\n })\n\n if (!bases.length) return null\n\n let commonAncestor = ''\n const parts = bases[0].split('/')\n for (let i = 0; i < parts.length; i++) {\n const candidate = parts.slice(0, i + 1).join('/')\n if (bases.every((b) => b.startsWith(candidate))) commonAncestor = candidate\n else break\n }\n if (!commonAncestor) commonAncestor = '/'\n return commonAncestor\n}\n","import { glob as tinyGlob } from 'tinyglobby'\nimport { getCommonBase } from './resolve'\nimport { slash } from './utils'\n\nexport interface MatchInput {\n globsResolved: string[]\n root: string\n importerFile: string\n exhaustive?: boolean\n}\n\nexport interface MatchResult {\n cwd: string\n dot: boolean\n ignore: string[]\n matches: string[]\n}\n\nexport async function matchGlobs(input: MatchInput): Promise<MatchResult> {\n const globsResolved = input.globsResolved.map(slash)\n const root = slash(input.root)\n const importerFile = slash(input.importerFile)\n const cwd = getCommonBase(globsResolved) ?? root\n const dot = !!input.exhaustive\n\n const ignore = dot ? [] : ['**/node_modules/**']\n const matches = (\n await tinyGlob(globsResolved, {\n cwd,\n absolute: true,\n dot,\n expandDirectories: false,\n ignore,\n })\n )\n .map(slash)\n .filter((f) => f !== importerFile)\n .sort()\n\n return {\n cwd,\n dot,\n ignore,\n matches,\n }\n}\n","import type { RollupError } from 'rollup'\nimport { slash } from './utils'\n\nexport interface EmptyGlobFailure {\n importer: string\n importerFile: string\n loc: { line: number; column: number } | null\n globs: string[]\n globsResolved: string[]\n exhaustive: boolean\n base?: string\n cwd: string\n ignore: string[]\n}\n\nexport function formatEmptyGlobFailure(f: EmptyGlobFailure): RollupError {\n const importerLabel =\n f.loc != null\n ? `${slash(f.importerFile)}:${f.loc.line}:${f.loc.column + 1}`\n : slash(f.importerFile)\n\n const lines: string[] = []\n lines.push('[glob-guard] Required glob matched 0 files')\n lines.push(` importer: ${importerLabel}`)\n if (f.globs.length === 1) {\n lines.push(` glob: ${f.globs[0]}`)\n } else {\n lines.push(' globs:')\n for (const g of f.globs) lines.push(` - ${g}`)\n }\n lines.push(' resolved:')\n for (const g of f.globsResolved) lines.push(` - ${g}`)\n lines.push(` options: exhaustive=${String(f.exhaustive)}${f.base ? ` base=${f.base}` : ''}`)\n lines.push(` cwd: ${f.cwd}`)\n if (f.ignore.length > 0) {\n lines.push(' ignore:')\n for (const i of f.ignore) lines.push(` - ${i}`)\n }\n lines.push(' hints:')\n lines.push(' - Did you rename/move the directory?')\n lines.push(' - Did you change file extensions?')\n lines.push(' - Are you excluding everything via a negated pattern (\"!...\")?')\n\n const err = new Error(lines.join('\\n')) as RollupError\n err.id = slash(f.importerFile)\n if (f.loc) err.loc = { file: err.id, line: f.loc.line, column: f.loc.column }\n return err\n}\n\nexport function formatManyFailures(failures: EmptyGlobFailure[]): RollupError {\n if (failures.length === 1) return formatEmptyGlobFailure(failures[0])\n\n const lines: string[] = []\n lines.push(`[glob-guard] ${failures.length} required globs matched 0 files`)\n for (const f of failures) {\n const importerLabel =\n f.loc != null\n ? `${slash(f.importerFile)}:${f.loc.line}:${f.loc.column + 1}`\n : slash(f.importerFile)\n lines.push(` - ${importerLabel}`)\n if (f.globs.length === 1) {\n lines.push(` glob: ${f.globs[0]}`)\n } else {\n lines.push(' globs:')\n for (const g of f.globs) lines.push(` - ${g}`)\n }\n }\n const err = new Error(lines.join('\\n')) as RollupError\n return err\n}\n","import type { Plugin } from 'vite'\nimport {\n parseRequiredGlobCalls,\n parseRequiredGlobCallsInAstroFrontmatter,\n parseRequiredGlobCallsInScriptTags,\n type ParsedGlobCallsite,\n} from './parse'\nimport { matchesAny, slash, stripQuery } from './utils'\nimport { toAbsoluteGlob } from './resolve'\nimport { matchGlobs } from './match'\nimport { formatManyFailures, type EmptyGlobFailure } from './diagnostics'\nimport type { GlobGuardOptions } from './types'\n\nconst DEFAULTS: Required<Pick<\n GlobGuardOptions,\n 'mode' | 'failOnBuild' | 'checkOnServe' | 'failOnServe' | 'report' | 'ignoreImporters'\n>> = {\n mode: 'annotated',\n failOnBuild: true,\n checkOnServe: false,\n failOnServe: false,\n report: false,\n ignoreImporters: [],\n}\n\nexport default function globGuard(userOptions: GlobGuardOptions = {}): Plugin {\n const options = { ...DEFAULTS, ...userOptions }\n\n let root = ''\n let isBuild = false\n\n const callsites: ParsedGlobCallsite[] = []\n\n async function checkAll(this: any) {\n if (callsites.length === 0) return\n\n const failures: EmptyGlobFailure[] = []\n\n for (const cs of callsites) {\n const importerFile = stripQuery(cs.importer)\n\n const globsResolved = await Promise.all(\n cs.globs.map((g) =>\n toAbsoluteGlob(g, {\n root,\n importer: slash(importerFile),\n base: cs.options.base,\n resolveId: async (id, importer) => {\n const r = await this.resolve(id, importer)\n return r?.id\n },\n }),\n ),\n )\n\n const match = await matchGlobs({\n globsResolved,\n root,\n importerFile,\n exhaustive: cs.options.exhaustive,\n })\n\n if (match.matches.length === 0) {\n failures.push({\n importer: cs.importer,\n importerFile,\n loc: cs.loc,\n globs: cs.globs,\n globsResolved,\n exhaustive: !!cs.options.exhaustive,\n base: cs.options.base,\n cwd: match.cwd,\n ignore: match.ignore,\n })\n }\n }\n\n if (failures.length === 0) return\n\n // Report (optional)\n if (options.report) {\n const reportFile =\n options.report === true\n ? 'glob-guard.report.json'\n : options.report.file\n const fs = await import('node:fs/promises')\n const payload = {\n root,\n failures: failures.map((f) => ({\n importer: slash(f.importerFile),\n loc: f.loc,\n globs: f.globs,\n globsResolved: f.globsResolved,\n exhaustive: f.exhaustive,\n base: f.base,\n cwd: f.cwd,\n ignore: f.ignore,\n })),\n }\n await fs.writeFile(reportFile, JSON.stringify(payload, null, 2), 'utf8')\n }\n\n const err = formatManyFailures(failures)\n if (isBuild ? options.failOnBuild : options.failOnServe) {\n this.error(err)\n } else {\n this.warn(err)\n }\n }\n\n return {\n name: 'glob-guard',\n\n // We must run before Vite's internal TS/JS transforms (esbuild), otherwise\n // comment annotations may be stripped before we can read them.\n enforce: 'pre',\n\n\n configResolved(config) {\n root = slash(config.root)\n isBuild = config.command === 'build'\n },\n\n transform(code, id) {\n if (matchesAny(id, options.ignoreImporters)) return null\n\n let parsed: ParsedGlobCallsite[]\n try {\n const file = stripQuery(id)\n if (file.endsWith('.vue') || file.endsWith('.svelte')) {\n parsed = parseRequiredGlobCallsInScriptTags(code, id, { mode: options.mode })\n } else if (file.endsWith('.astro')) {\n parsed = parseRequiredGlobCallsInAstroFrontmatter(code, id, { mode: options.mode })\n } else {\n parsed = parseRequiredGlobCalls(code, id, { mode: options.mode })\n }\n } catch {\n // Some files (e.g. Vue/Svelte SFC source) are not valid JS/TS before\n // their framework plugin runs. We intentionally ignore parse failures\n // and only act on call sites we can confidently extract.\n return null\n }\n if (parsed.length > 0) callsites.push(...parsed)\n return null\n },\n\n async buildEnd() {\n await checkAll.call(this)\n },\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vite-plugin-glob-guard",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Opt-in guardrails for import.meta.glob() required globs",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=20.19.0"
|
|
10
|
+
},
|
|
11
|
+
"main": "./dist/index.cjs",
|
|
12
|
+
"module": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js",
|
|
18
|
+
"require": "./dist/index.cjs"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup",
|
|
26
|
+
"dev": "tsup --watch",
|
|
27
|
+
"release": "node scripts/release.mjs",
|
|
28
|
+
"release:dry": "node scripts/release.mjs --dry-run",
|
|
29
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"test:watch": "vitest",
|
|
32
|
+
"prepublishOnly": "npm run build && npm run test"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"vite",
|
|
36
|
+
"vite-plugin",
|
|
37
|
+
"import-meta-glob",
|
|
38
|
+
"glob",
|
|
39
|
+
"tooling"
|
|
40
|
+
],
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@babel/parser": "^7.29.0",
|
|
43
|
+
"@babel/traverse": "^7.29.0",
|
|
44
|
+
"picomatch": "^4.0.3",
|
|
45
|
+
"tinyglobby": "^0.2.15"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
49
|
+
"@types/babel__traverse": "^7.28.0",
|
|
50
|
+
"@types/node": "^25.2.0",
|
|
51
|
+
"@types/picomatch": "^4.0.2",
|
|
52
|
+
"@vitejs/plugin-react": "^5.1.3",
|
|
53
|
+
"@vitejs/plugin-vue": "^6.0.4",
|
|
54
|
+
"react": "^19.2.4",
|
|
55
|
+
"react-dom": "^19.2.4",
|
|
56
|
+
"svelte": "^5.49.1",
|
|
57
|
+
"tsup": "^8.5.1",
|
|
58
|
+
"typescript": "^5.9.3",
|
|
59
|
+
"vite": "^7.3.1",
|
|
60
|
+
"vue": "^3.5.27",
|
|
61
|
+
"vitest": "^4.0.18"
|
|
62
|
+
},
|
|
63
|
+
"peerDependencies": {
|
|
64
|
+
"vite": ">=7.0.0"
|
|
65
|
+
}
|
|
66
|
+
}
|