@openpolicy/vite-auto-collect 0.0.18
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/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +310 -0
- package/dist/index.js.map +1 -0
- package/package.json +42 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Plugin } from "vite";
|
|
2
|
+
|
|
3
|
+
//#region src/index.d.ts
|
|
4
|
+
type AutoCollectOptions = {
|
|
5
|
+
/**
|
|
6
|
+
* Directory walked for `collecting()` calls. Resolved relative to the
|
|
7
|
+
* Vite project root. Defaults to `"src"`.
|
|
8
|
+
*/
|
|
9
|
+
srcDir?: string;
|
|
10
|
+
/**
|
|
11
|
+
* File extensions scanned. Defaults to `[".ts", ".tsx"]`.
|
|
12
|
+
*/
|
|
13
|
+
extensions?: string[];
|
|
14
|
+
/**
|
|
15
|
+
* Extra directory names skipped during the walk. Appended to the built-in
|
|
16
|
+
* defaults (`node_modules`, `dist`, `.git`, `.next`, `.output`,
|
|
17
|
+
* `.svelte-kit`, `.cache`).
|
|
18
|
+
*/
|
|
19
|
+
ignore?: string[];
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Vite plugin that scans source files for `@openpolicy/sdk` `collecting()`
|
|
23
|
+
* calls at the start of each build and inlines the discovered categories into
|
|
24
|
+
* the SDK's `dataCollected` sentinel.
|
|
25
|
+
*
|
|
26
|
+
* Internally the plugin intercepts `@openpolicy/sdk`'s own relative import of
|
|
27
|
+
* `./auto-collected` and redirects it to a virtual module whose body is a
|
|
28
|
+
* literal `export const dataCollected = { ... }`. Because the replacement
|
|
29
|
+
* becomes part of the consumer's own module graph, the scanned data survives
|
|
30
|
+
* any downstream bundler boundary (e.g. nitro's SSR output), which a shared
|
|
31
|
+
* module-level registry would not.
|
|
32
|
+
*/
|
|
33
|
+
declare function autoCollect(options?: AutoCollectOptions): Plugin;
|
|
34
|
+
//#endregion
|
|
35
|
+
export { AutoCollectOptions, autoCollect };
|
|
36
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;KAMY,kBAAA;;AAAZ;;;EAKC,MAAA;EAAA;;;EAIA,UAAA;EAMM;AAwCP;;;;EAxCC,MAAA;AAAA;;;;;;;;;;;;;iBAwCe,WAAA,CAAY,OAAA,GAAS,kBAAA,GAA0B,MAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
2
|
+
import { extname, join, relative, resolve } from "node:path";
|
|
3
|
+
import { parseSync } from "oxc-parser";
|
|
4
|
+
//#region src/analyse.ts
|
|
5
|
+
const SDK_SPECIFIER = "@openpolicy/sdk";
|
|
6
|
+
const COLLECTING_NAME = "collecting";
|
|
7
|
+
/**
|
|
8
|
+
* Extract `collecting()` call metadata from a single source file.
|
|
9
|
+
*
|
|
10
|
+
* Returns a `Record<category, labels[]>` for every detected call whose
|
|
11
|
+
* arguments match the analysable shape. Files with no matching calls — or
|
|
12
|
+
* that fail to parse — return `{}`.
|
|
13
|
+
*
|
|
14
|
+
* The analyser runs in two phases:
|
|
15
|
+
* 1. Collect local names bound to `collecting` imported from `@openpolicy/sdk`
|
|
16
|
+
* (handles renamed imports, skips type-only imports, ignores look-alikes
|
|
17
|
+
* imported from other modules).
|
|
18
|
+
* 2. Walk the program body and inspect every `CallExpression` whose callee
|
|
19
|
+
* is one of those tracked local names.
|
|
20
|
+
*/
|
|
21
|
+
function extractCollecting(filename, code) {
|
|
22
|
+
let result;
|
|
23
|
+
try {
|
|
24
|
+
result = parseSync(filename, code);
|
|
25
|
+
} catch {
|
|
26
|
+
console.warn(`[openpolicy-auto-collect] parse error in ${filename}`);
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
if (result.errors.length > 0) {
|
|
30
|
+
if (result.errors.some((e) => e.severity === "Error")) {
|
|
31
|
+
console.warn(`[openpolicy-auto-collect] parse error in ${filename}`);
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const program = result.program;
|
|
36
|
+
const localNames = collectSdkCollectingBindings(program);
|
|
37
|
+
if (localNames.size === 0) return {};
|
|
38
|
+
const out = {};
|
|
39
|
+
walk(program, (node) => {
|
|
40
|
+
if (node.type !== "CallExpression") return;
|
|
41
|
+
const callee = node.callee;
|
|
42
|
+
if (!callee || callee.type !== "Identifier") return;
|
|
43
|
+
if (!localNames.has(callee.name)) return;
|
|
44
|
+
const args = node.arguments;
|
|
45
|
+
if (!args || args.length < 3) return;
|
|
46
|
+
const category = extractStringLiteral(args[0]);
|
|
47
|
+
if (category === null) return;
|
|
48
|
+
const labels = extractLabelKeys(args[2]);
|
|
49
|
+
if (labels === null) return;
|
|
50
|
+
const existing = out[category] ?? [];
|
|
51
|
+
const seen = new Set(existing);
|
|
52
|
+
for (const label of labels) if (!seen.has(label)) {
|
|
53
|
+
existing.push(label);
|
|
54
|
+
seen.add(label);
|
|
55
|
+
}
|
|
56
|
+
out[category] = existing;
|
|
57
|
+
});
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Walk `ImportDeclaration` nodes and return the local names bound to
|
|
62
|
+
* `collecting` imported from `@openpolicy/sdk`. Skips type-only imports and
|
|
63
|
+
* specifiers whose imported name isn't literally `collecting`.
|
|
64
|
+
*/
|
|
65
|
+
function collectSdkCollectingBindings(program) {
|
|
66
|
+
const names = /* @__PURE__ */ new Set();
|
|
67
|
+
const body = program.body;
|
|
68
|
+
if (!body) return names;
|
|
69
|
+
for (const node of body) {
|
|
70
|
+
if (node.type !== "ImportDeclaration") continue;
|
|
71
|
+
if (node.importKind === "type") continue;
|
|
72
|
+
const source = node.source;
|
|
73
|
+
if (!source || source.value !== SDK_SPECIFIER) continue;
|
|
74
|
+
const specifiers = node.specifiers;
|
|
75
|
+
if (!specifiers) continue;
|
|
76
|
+
for (const spec of specifiers) {
|
|
77
|
+
if (spec.type !== "ImportSpecifier") continue;
|
|
78
|
+
if (spec.importKind === "type") continue;
|
|
79
|
+
const imported = spec.imported;
|
|
80
|
+
if (!imported) continue;
|
|
81
|
+
if ((imported.type === "Identifier" ? imported.name : imported.type === "Literal" ? typeof imported.value === "string" ? imported.value : void 0 : void 0) !== COLLECTING_NAME) continue;
|
|
82
|
+
const local = spec.local;
|
|
83
|
+
if (!local || local.type !== "Identifier") continue;
|
|
84
|
+
names.add(local.name);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return names;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* If `node` is a string `Literal`, return its string value. Otherwise
|
|
91
|
+
* return `null` so the caller silently skips the call.
|
|
92
|
+
*/
|
|
93
|
+
function extractStringLiteral(node) {
|
|
94
|
+
if (!node) return null;
|
|
95
|
+
if (node.type !== "Literal") return null;
|
|
96
|
+
if (typeof node.value !== "string") return null;
|
|
97
|
+
return node.value;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Extract the string values from a plain `{ fieldName: "Human Label" }`
|
|
101
|
+
* object literal. Returns an array of label strings, deduped while
|
|
102
|
+
* preserving insertion order. Returns `null` if the shape doesn't match.
|
|
103
|
+
*/
|
|
104
|
+
function extractLabelKeys(node) {
|
|
105
|
+
if (!node || node.type !== "ObjectExpression") return null;
|
|
106
|
+
const properties = node.properties;
|
|
107
|
+
if (!properties) return null;
|
|
108
|
+
const labels = [];
|
|
109
|
+
const seen = /* @__PURE__ */ new Set();
|
|
110
|
+
for (const prop of properties) {
|
|
111
|
+
if (prop.type !== "Property") continue;
|
|
112
|
+
const val = prop.value;
|
|
113
|
+
if (!val || val.type !== "Literal" || typeof val.value !== "string") continue;
|
|
114
|
+
if (seen.has(val.value)) continue;
|
|
115
|
+
seen.add(val.value);
|
|
116
|
+
labels.push(val.value);
|
|
117
|
+
}
|
|
118
|
+
return labels;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Recursive AST walker. Visits every `AnyNode` (depth-first) reachable
|
|
122
|
+
* through array / nested-object children and invokes `visit` on each.
|
|
123
|
+
*/
|
|
124
|
+
function walk(node, visit) {
|
|
125
|
+
visit(node);
|
|
126
|
+
for (const key of Object.keys(node)) {
|
|
127
|
+
if (key === "parent") continue;
|
|
128
|
+
const value = node[key];
|
|
129
|
+
if (Array.isArray(value)) {
|
|
130
|
+
for (const item of value) if (item && typeof item === "object" && typeof item.type === "string") walk(item, visit);
|
|
131
|
+
} else if (value && typeof value === "object" && typeof value.type === "string") walk(value, visit);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//#endregion
|
|
135
|
+
//#region src/scan.ts
|
|
136
|
+
const DEFAULT_IGNORES = new Set([
|
|
137
|
+
"node_modules",
|
|
138
|
+
"dist",
|
|
139
|
+
".git",
|
|
140
|
+
".next",
|
|
141
|
+
".output",
|
|
142
|
+
".svelte-kit",
|
|
143
|
+
".cache"
|
|
144
|
+
]);
|
|
145
|
+
/**
|
|
146
|
+
* Recursively walks `root`, returning absolute paths of every regular file
|
|
147
|
+
* whose extension is in `extensions`. Directories whose basename appears in
|
|
148
|
+
* the built-in ignore list (or the extra `ignore` argument) are skipped
|
|
149
|
+
* entirely.
|
|
150
|
+
*
|
|
151
|
+
* Missing roots resolve to an empty array — the plugin must not throw if the
|
|
152
|
+
* user's `srcDir` hasn't been created yet.
|
|
153
|
+
*/
|
|
154
|
+
async function walkSources(root, extensions, ignore = []) {
|
|
155
|
+
const ignored = new Set([...DEFAULT_IGNORES, ...ignore]);
|
|
156
|
+
const exts = new Set(extensions);
|
|
157
|
+
const results = [];
|
|
158
|
+
async function walk(dir) {
|
|
159
|
+
let entries;
|
|
160
|
+
try {
|
|
161
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
162
|
+
} catch (err) {
|
|
163
|
+
const code = err.code;
|
|
164
|
+
if (code === "ENOENT" || code === "ENOTDIR") return;
|
|
165
|
+
throw err;
|
|
166
|
+
}
|
|
167
|
+
for (const entry of entries) {
|
|
168
|
+
if (ignored.has(entry.name)) continue;
|
|
169
|
+
const full = join(dir, entry.name);
|
|
170
|
+
if (entry.isDirectory()) await walk(full);
|
|
171
|
+
else if (entry.isFile() && exts.has(extname(entry.name))) results.push(full);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
await walk(root);
|
|
175
|
+
results.sort();
|
|
176
|
+
return results;
|
|
177
|
+
}
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region src/index.ts
|
|
180
|
+
/**
|
|
181
|
+
* Marker returned from `resolveId` so `load` can recognise a hit. The leading
|
|
182
|
+
* NUL prefix is the Rollup/Vite convention for virtual IDs so other plugins
|
|
183
|
+
* leave it alone.
|
|
184
|
+
*/
|
|
185
|
+
const RESOLVED_VIRTUAL_ID = "\0virtual:openpolicy/auto-collected";
|
|
186
|
+
/**
|
|
187
|
+
* Matches any path that lives inside the `@openpolicy/sdk` package, whether
|
|
188
|
+
* it's resolved via a workspace symlink (`.../packages/sdk/...`) or a
|
|
189
|
+
* published `node_modules` install (`.../@openpolicy/sdk/...`). Used to scope
|
|
190
|
+
* the `./auto-collected` relative-import interception to the SDK itself.
|
|
191
|
+
*/
|
|
192
|
+
const SDK_PATH_PATTERN = /[\\/](?:@openpolicy[\\/]sdk|packages[\\/]sdk)[\\/]/;
|
|
193
|
+
/**
|
|
194
|
+
* Matches the relative specifier the SDK uses for its own internal
|
|
195
|
+
* `./auto-collected` import. Both the source form (`./auto-collected`) and
|
|
196
|
+
* the published dist form (`./auto-collected.js`) need to be intercepted:
|
|
197
|
+
* the former applies when consumers resolve the SDK via its workspace
|
|
198
|
+
* source, the latter when resolving against `dist/` with the separate
|
|
199
|
+
* `auto-collected.js` chunk.
|
|
200
|
+
*/
|
|
201
|
+
const AUTO_COLLECTED_SPECIFIER = /^\.\/auto-collected(?:\.js)?$/;
|
|
202
|
+
/**
|
|
203
|
+
* Vite plugin that scans source files for `@openpolicy/sdk` `collecting()`
|
|
204
|
+
* calls at the start of each build and inlines the discovered categories into
|
|
205
|
+
* the SDK's `dataCollected` sentinel.
|
|
206
|
+
*
|
|
207
|
+
* Internally the plugin intercepts `@openpolicy/sdk`'s own relative import of
|
|
208
|
+
* `./auto-collected` and redirects it to a virtual module whose body is a
|
|
209
|
+
* literal `export const dataCollected = { ... }`. Because the replacement
|
|
210
|
+
* becomes part of the consumer's own module graph, the scanned data survives
|
|
211
|
+
* any downstream bundler boundary (e.g. nitro's SSR output), which a shared
|
|
212
|
+
* module-level registry would not.
|
|
213
|
+
*/
|
|
214
|
+
function autoCollect(options = {}) {
|
|
215
|
+
const srcDirOpt = options.srcDir ?? "src";
|
|
216
|
+
const extensions = options.extensions ?? [".ts", ".tsx"];
|
|
217
|
+
const ignore = options.ignore ?? [];
|
|
218
|
+
let resolvedSrcDir;
|
|
219
|
+
let scanned = {};
|
|
220
|
+
async function scanAndMerge() {
|
|
221
|
+
const files = await walkSources(resolvedSrcDir, extensions, ignore);
|
|
222
|
+
const merged = {};
|
|
223
|
+
for (const file of files) {
|
|
224
|
+
let code;
|
|
225
|
+
try {
|
|
226
|
+
code = await readFile(file, "utf8");
|
|
227
|
+
} catch {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const extracted = extractCollecting(file, code);
|
|
231
|
+
for (const [category, labels] of Object.entries(extracted)) {
|
|
232
|
+
const existing = merged[category] ?? [];
|
|
233
|
+
const seen = new Set(existing);
|
|
234
|
+
for (const label of labels) if (!seen.has(label)) {
|
|
235
|
+
existing.push(label);
|
|
236
|
+
seen.add(label);
|
|
237
|
+
}
|
|
238
|
+
merged[category] = existing;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return merged;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Returns true when `file` lives inside `resolvedSrcDir` and has one of
|
|
245
|
+
* the tracked extensions. Used by the dev-server watcher to skip events
|
|
246
|
+
* for unrelated files (configs, public assets, other packages, etc.).
|
|
247
|
+
*/
|
|
248
|
+
function isTrackedSource(file) {
|
|
249
|
+
const rel = relative(resolvedSrcDir, file);
|
|
250
|
+
if (!rel || rel.startsWith("..")) return false;
|
|
251
|
+
return extensions.some((ext) => file.endsWith(ext));
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Re-runs the scan and, if anything changed, invalidates the virtual
|
|
255
|
+
* module and triggers a full page reload. A full reload is used because
|
|
256
|
+
* `dataCollected` is spread into the policy config at module-evaluation
|
|
257
|
+
* time and the result is captured by the React tree as a prop — there's
|
|
258
|
+
* no clean way to hot-swap it in place.
|
|
259
|
+
*/
|
|
260
|
+
async function rescanAndRefresh(server) {
|
|
261
|
+
const next = await scanAndMerge();
|
|
262
|
+
if (JSON.stringify(next) === JSON.stringify(scanned)) return;
|
|
263
|
+
scanned = next;
|
|
264
|
+
const mod = server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ID);
|
|
265
|
+
if (mod) server.moduleGraph.invalidateModule(mod);
|
|
266
|
+
server.ws.send({ type: "full-reload" });
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
name: "openpolicy-auto-collect",
|
|
270
|
+
enforce: "pre",
|
|
271
|
+
configResolved(config) {
|
|
272
|
+
resolvedSrcDir = resolve(config.root, srcDirOpt);
|
|
273
|
+
},
|
|
274
|
+
async buildStart() {
|
|
275
|
+
scanned = await scanAndMerge();
|
|
276
|
+
},
|
|
277
|
+
configureServer(server) {
|
|
278
|
+
server.watcher.add(resolvedSrcDir);
|
|
279
|
+
const handler = async (file) => {
|
|
280
|
+
if (!isTrackedSource(file)) return;
|
|
281
|
+
try {
|
|
282
|
+
await rescanAndRefresh(server);
|
|
283
|
+
} catch (error) {
|
|
284
|
+
server.config.logger.error(`[openpolicy-auto-collect] rescan failed: ${error}`);
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
server.watcher.on("change", handler);
|
|
288
|
+
server.watcher.on("add", handler);
|
|
289
|
+
server.watcher.on("unlink", handler);
|
|
290
|
+
},
|
|
291
|
+
async resolveId(source, importer, resolveOptions) {
|
|
292
|
+
if (!importer || !AUTO_COLLECTED_SPECIFIER.test(source)) return null;
|
|
293
|
+
const resolved = await this.resolve(source, importer, {
|
|
294
|
+
...resolveOptions,
|
|
295
|
+
skipSelf: true
|
|
296
|
+
});
|
|
297
|
+
if (!resolved) return null;
|
|
298
|
+
if (!SDK_PATH_PATTERN.test(resolved.id)) return null;
|
|
299
|
+
return RESOLVED_VIRTUAL_ID;
|
|
300
|
+
},
|
|
301
|
+
load(id) {
|
|
302
|
+
if (id !== RESOLVED_VIRTUAL_ID) return null;
|
|
303
|
+
return `export const dataCollected = ${JSON.stringify(scanned)};\n`;
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
//#endregion
|
|
308
|
+
export { autoCollect };
|
|
309
|
+
|
|
310
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/analyse.ts","../src/scan.ts","../src/index.ts"],"sourcesContent":["import { parseSync } from \"oxc-parser\";\n\nconst SDK_SPECIFIER = \"@openpolicy/sdk\";\nconst COLLECTING_NAME = \"collecting\";\n\ntype AnyNode = { type: string; [key: string]: unknown };\n\n/**\n * Extract `collecting()` call metadata from a single source file.\n *\n * Returns a `Record<category, labels[]>` for every detected call whose\n * arguments match the analysable shape. Files with no matching calls — or\n * that fail to parse — return `{}`.\n *\n * The analyser runs in two phases:\n * 1. Collect local names bound to `collecting` imported from `@openpolicy/sdk`\n * (handles renamed imports, skips type-only imports, ignores look-alikes\n * imported from other modules).\n * 2. Walk the program body and inspect every `CallExpression` whose callee\n * is one of those tracked local names.\n */\nexport function extractCollecting(\n\tfilename: string,\n\tcode: string,\n): Record<string, string[]> {\n\tlet result: ReturnType<typeof parseSync>;\n\ttry {\n\t\tresult = parseSync(filename, code);\n\t} catch {\n\t\tconsole.warn(`[openpolicy-auto-collect] parse error in ${filename}`);\n\t\treturn {};\n\t}\n\n\tif (result.errors.length > 0) {\n\t\t// Hard parse failures only — oxc reports recoverable errors but still\n\t\t// produces a usable AST, so we keep going and let the walker decide.\n\t\tconst fatal = result.errors.some((e) => e.severity === (\"Error\" as never));\n\t\tif (fatal) {\n\t\t\tconsole.warn(`[openpolicy-auto-collect] parse error in ${filename}`);\n\t\t\treturn {};\n\t\t}\n\t}\n\n\tconst program = result.program as unknown as AnyNode;\n\tconst localNames = collectSdkCollectingBindings(program);\n\tif (localNames.size === 0) return {};\n\n\tconst out: Record<string, string[]> = {};\n\twalk(program, (node) => {\n\t\tif (node.type !== \"CallExpression\") return;\n\t\tconst callee = node.callee as AnyNode | undefined;\n\t\tif (!callee || callee.type !== \"Identifier\") return;\n\t\tif (!localNames.has(callee.name as string)) return;\n\n\t\tconst args = node.arguments as AnyNode[] | undefined;\n\t\tif (!args || args.length < 3) return;\n\n\t\tconst category = extractStringLiteral(args[0]);\n\t\tif (category === null) return;\n\n\t\tconst labels = extractLabelKeys(args[2]);\n\t\tif (labels === null) return;\n\n\t\tconst existing = out[category] ?? [];\n\t\tconst seen = new Set(existing);\n\t\tfor (const label of labels) {\n\t\t\tif (!seen.has(label)) {\n\t\t\t\texisting.push(label);\n\t\t\t\tseen.add(label);\n\t\t\t}\n\t\t}\n\t\tout[category] = existing;\n\t});\n\treturn out;\n}\n\n/**\n * Walk `ImportDeclaration` nodes and return the local names bound to\n * `collecting` imported from `@openpolicy/sdk`. Skips type-only imports and\n * specifiers whose imported name isn't literally `collecting`.\n */\nfunction collectSdkCollectingBindings(program: AnyNode): Set<string> {\n\tconst names = new Set<string>();\n\tconst body = program.body as AnyNode[] | undefined;\n\tif (!body) return names;\n\tfor (const node of body) {\n\t\tif (node.type !== \"ImportDeclaration\") continue;\n\t\tif ((node.importKind as string | undefined) === \"type\") continue;\n\t\tconst source = node.source as AnyNode | undefined;\n\t\tif (!source || source.value !== SDK_SPECIFIER) continue;\n\t\tconst specifiers = node.specifiers as AnyNode[] | undefined;\n\t\tif (!specifiers) continue;\n\t\tfor (const spec of specifiers) {\n\t\t\tif (spec.type !== \"ImportSpecifier\") continue;\n\t\t\tif ((spec.importKind as string | undefined) === \"type\") continue;\n\t\t\tconst imported = spec.imported as AnyNode | undefined;\n\t\t\tif (!imported) continue;\n\t\t\t// ESTree allows either an Identifier (name) or a string Literal (value)\n\t\t\t// for the imported binding (e.g. `import { \"foo\" as bar }`).\n\t\t\tconst importedName =\n\t\t\t\timported.type === \"Identifier\"\n\t\t\t\t\t? (imported.name as string | undefined)\n\t\t\t\t\t: imported.type === \"Literal\"\n\t\t\t\t\t\t? typeof imported.value === \"string\"\n\t\t\t\t\t\t\t? imported.value\n\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t\t: undefined;\n\t\t\tif (importedName !== COLLECTING_NAME) continue;\n\t\t\tconst local = spec.local as AnyNode | undefined;\n\t\t\tif (!local || local.type !== \"Identifier\") continue;\n\t\t\tnames.add(local.name as string);\n\t\t}\n\t}\n\treturn names;\n}\n\n/**\n * If `node` is a string `Literal`, return its string value. Otherwise\n * return `null` so the caller silently skips the call.\n */\nfunction extractStringLiteral(node: AnyNode | undefined): string | null {\n\tif (!node) return null;\n\tif (node.type !== \"Literal\") return null;\n\tif (typeof node.value !== \"string\") return null;\n\treturn node.value;\n}\n\n/**\n * Extract the string values from a plain `{ fieldName: \"Human Label\" }`\n * object literal. Returns an array of label strings, deduped while\n * preserving insertion order. Returns `null` if the shape doesn't match.\n */\nfunction extractLabelKeys(node: AnyNode | undefined): string[] | null {\n\tif (!node || node.type !== \"ObjectExpression\") return null;\n\tconst properties = node.properties as AnyNode[] | undefined;\n\tif (!properties) return null;\n\n\tconst labels: string[] = [];\n\tconst seen = new Set<string>();\n\tfor (const prop of properties) {\n\t\tif (prop.type !== \"Property\") continue; // drop SpreadElement silently\n\t\tconst val = prop.value as AnyNode | undefined;\n\t\tif (!val || val.type !== \"Literal\" || typeof val.value !== \"string\")\n\t\t\tcontinue;\n\t\tif (seen.has(val.value)) continue;\n\t\tseen.add(val.value);\n\t\tlabels.push(val.value);\n\t}\n\treturn labels;\n}\n\n/**\n * Recursive AST walker. Visits every `AnyNode` (depth-first) reachable\n * through array / nested-object children and invokes `visit` on each.\n */\nfunction walk(node: AnyNode, visit: (node: AnyNode) => void): void {\n\tvisit(node);\n\tfor (const key of Object.keys(node)) {\n\t\tif (key === \"parent\") continue;\n\t\tconst value = node[key];\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const item of value) {\n\t\t\t\tif (item && typeof item === \"object\" && typeof item.type === \"string\") {\n\t\t\t\t\twalk(item as AnyNode, visit);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (\n\t\t\tvalue &&\n\t\t\ttypeof value === \"object\" &&\n\t\t\ttypeof (value as { type?: unknown }).type === \"string\"\n\t\t) {\n\t\t\twalk(value as AnyNode, visit);\n\t\t}\n\t}\n}\n","import type { Dirent } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport { extname, join } from \"node:path\";\n\nconst DEFAULT_IGNORES: ReadonlySet<string> = new Set([\n\t\"node_modules\",\n\t\"dist\",\n\t\".git\",\n\t\".next\",\n\t\".output\",\n\t\".svelte-kit\",\n\t\".cache\",\n]);\n\n/**\n * Recursively walks `root`, returning absolute paths of every regular file\n * whose extension is in `extensions`. Directories whose basename appears in\n * the built-in ignore list (or the extra `ignore` argument) are skipped\n * entirely.\n *\n * Missing roots resolve to an empty array — the plugin must not throw if the\n * user's `srcDir` hasn't been created yet.\n */\nexport async function walkSources(\n\troot: string,\n\textensions: readonly string[],\n\tignore: readonly string[] = [],\n): Promise<string[]> {\n\tconst ignored = new Set<string>([...DEFAULT_IGNORES, ...ignore]);\n\tconst exts = new Set(extensions);\n\tconst results: string[] = [];\n\n\tasync function walk(dir: string): Promise<void> {\n\t\tlet entries: Dirent[];\n\t\ttry {\n\t\t\tentries = (await readdir(dir, { withFileTypes: true })) as Dirent[];\n\t\t} catch (err) {\n\t\t\tconst code = (err as NodeJS.ErrnoException).code;\n\t\t\tif (code === \"ENOENT\" || code === \"ENOTDIR\") return;\n\t\t\tthrow err;\n\t\t}\n\t\tfor (const entry of entries) {\n\t\t\tif (ignored.has(entry.name)) continue;\n\t\t\tconst full = join(dir, entry.name);\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tawait walk(full);\n\t\t\t} else if (entry.isFile() && exts.has(extname(entry.name))) {\n\t\t\t\tresults.push(full);\n\t\t\t}\n\t\t}\n\t}\n\n\tawait walk(root);\n\tresults.sort();\n\treturn results;\n}\n","import { readFile } from \"node:fs/promises\";\nimport { relative, resolve } from \"node:path\";\nimport type { Plugin, ViteDevServer } from \"vite\";\nimport { extractCollecting } from \"./analyse\";\nimport { walkSources } from \"./scan\";\n\nexport type AutoCollectOptions = {\n\t/**\n\t * Directory walked for `collecting()` calls. Resolved relative to the\n\t * Vite project root. Defaults to `\"src\"`.\n\t */\n\tsrcDir?: string;\n\t/**\n\t * File extensions scanned. Defaults to `[\".ts\", \".tsx\"]`.\n\t */\n\textensions?: string[];\n\t/**\n\t * Extra directory names skipped during the walk. Appended to the built-in\n\t * defaults (`node_modules`, `dist`, `.git`, `.next`, `.output`,\n\t * `.svelte-kit`, `.cache`).\n\t */\n\tignore?: string[];\n};\n\n/**\n * Marker returned from `resolveId` so `load` can recognise a hit. The leading\n * NUL prefix is the Rollup/Vite convention for virtual IDs so other plugins\n * leave it alone.\n */\nconst RESOLVED_VIRTUAL_ID = \"\\0virtual:openpolicy/auto-collected\";\n\n/**\n * Matches any path that lives inside the `@openpolicy/sdk` package, whether\n * it's resolved via a workspace symlink (`.../packages/sdk/...`) or a\n * published `node_modules` install (`.../@openpolicy/sdk/...`). Used to scope\n * the `./auto-collected` relative-import interception to the SDK itself.\n */\nconst SDK_PATH_PATTERN = /[\\\\/](?:@openpolicy[\\\\/]sdk|packages[\\\\/]sdk)[\\\\/]/;\n\n/**\n * Matches the relative specifier the SDK uses for its own internal\n * `./auto-collected` import. Both the source form (`./auto-collected`) and\n * the published dist form (`./auto-collected.js`) need to be intercepted:\n * the former applies when consumers resolve the SDK via its workspace\n * source, the latter when resolving against `dist/` with the separate\n * `auto-collected.js` chunk.\n */\nconst AUTO_COLLECTED_SPECIFIER = /^\\.\\/auto-collected(?:\\.js)?$/;\n\n/**\n * Vite plugin that scans source files for `@openpolicy/sdk` `collecting()`\n * calls at the start of each build and inlines the discovered categories into\n * the SDK's `dataCollected` sentinel.\n *\n * Internally the plugin intercepts `@openpolicy/sdk`'s own relative import of\n * `./auto-collected` and redirects it to a virtual module whose body is a\n * literal `export const dataCollected = { ... }`. Because the replacement\n * becomes part of the consumer's own module graph, the scanned data survives\n * any downstream bundler boundary (e.g. nitro's SSR output), which a shared\n * module-level registry would not.\n */\nexport function autoCollect(options: AutoCollectOptions = {}): Plugin {\n\tconst srcDirOpt = options.srcDir ?? \"src\";\n\tconst extensions = options.extensions ?? [\".ts\", \".tsx\"];\n\tconst ignore = options.ignore ?? [];\n\tlet resolvedSrcDir: string;\n\tlet scanned: Record<string, string[]> = {};\n\n\tasync function scanAndMerge(): Promise<Record<string, string[]>> {\n\t\tconst files = await walkSources(resolvedSrcDir, extensions, ignore);\n\t\tconst merged: Record<string, string[]> = {};\n\t\tfor (const file of files) {\n\t\t\tlet code: string;\n\t\t\ttry {\n\t\t\t\tcode = await readFile(file, \"utf8\");\n\t\t\t} catch {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst extracted = extractCollecting(file, code);\n\t\t\tfor (const [category, labels] of Object.entries(extracted)) {\n\t\t\t\tconst existing = merged[category] ?? [];\n\t\t\t\tconst seen = new Set(existing);\n\t\t\t\tfor (const label of labels) {\n\t\t\t\t\tif (!seen.has(label)) {\n\t\t\t\t\t\texisting.push(label);\n\t\t\t\t\t\tseen.add(label);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmerged[category] = existing;\n\t\t\t}\n\t\t}\n\t\treturn merged;\n\t}\n\n\t/**\n\t * Returns true when `file` lives inside `resolvedSrcDir` and has one of\n\t * the tracked extensions. Used by the dev-server watcher to skip events\n\t * for unrelated files (configs, public assets, other packages, etc.).\n\t */\n\tfunction isTrackedSource(file: string): boolean {\n\t\tconst rel = relative(resolvedSrcDir, file);\n\t\tif (!rel || rel.startsWith(\"..\")) return false;\n\t\treturn extensions.some((ext) => file.endsWith(ext));\n\t}\n\n\t/**\n\t * Re-runs the scan and, if anything changed, invalidates the virtual\n\t * module and triggers a full page reload. A full reload is used because\n\t * `dataCollected` is spread into the policy config at module-evaluation\n\t * time and the result is captured by the React tree as a prop — there's\n\t * no clean way to hot-swap it in place.\n\t */\n\tasync function rescanAndRefresh(server: ViteDevServer): Promise<void> {\n\t\tconst next = await scanAndMerge();\n\t\tif (JSON.stringify(next) === JSON.stringify(scanned)) return;\n\t\tscanned = next;\n\t\tconst mod = server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ID);\n\t\tif (mod) server.moduleGraph.invalidateModule(mod);\n\t\tserver.ws.send({ type: \"full-reload\" });\n\t}\n\n\treturn {\n\t\tname: \"openpolicy-auto-collect\",\n\t\tenforce: \"pre\",\n\t\tconfigResolved(config) {\n\t\t\tresolvedSrcDir = resolve(config.root, srcDirOpt);\n\t\t},\n\t\tasync buildStart() {\n\t\t\tscanned = await scanAndMerge();\n\t\t},\n\t\tconfigureServer(server) {\n\t\t\t// Make sure chokidar watches the whole src tree, not just files\n\t\t\t// already in the module graph. Without this, creating a brand-new\n\t\t\t// source file that nothing imports yet wouldn't fire a watcher\n\t\t\t// event — the very case we most need to re-scan on.\n\t\t\tserver.watcher.add(resolvedSrcDir);\n\n\t\t\tconst handler = async (file: string): Promise<void> => {\n\t\t\t\tif (!isTrackedSource(file)) return;\n\t\t\t\t// Surface errors via the logger but don't rethrow — an\n\t\t\t\t// unhandled rejection would crash the watcher process.\n\t\t\t\ttry {\n\t\t\t\t\tawait rescanAndRefresh(server);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tserver.config.logger.error(\n\t\t\t\t\t\t`[openpolicy-auto-collect] rescan failed: ${error}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tserver.watcher.on(\"change\", handler);\n\t\t\tserver.watcher.on(\"add\", handler);\n\t\t\tserver.watcher.on(\"unlink\", handler);\n\t\t},\n\t\tasync resolveId(source, importer, resolveOptions) {\n\t\t\tif (!importer || !AUTO_COLLECTED_SPECIFIER.test(source)) return null;\n\t\t\t// Defer to Vite's resolver first so we only redirect when the\n\t\t\t// relative specifier actually points inside @openpolicy/sdk.\n\t\t\tconst resolved = await this.resolve(source, importer, {\n\t\t\t\t...resolveOptions,\n\t\t\t\tskipSelf: true,\n\t\t\t});\n\t\t\tif (!resolved) return null;\n\t\t\tif (!SDK_PATH_PATTERN.test(resolved.id)) return null;\n\t\t\treturn RESOLVED_VIRTUAL_ID;\n\t\t},\n\t\tload(id) {\n\t\t\tif (id !== RESOLVED_VIRTUAL_ID) return null;\n\t\t\treturn `export const dataCollected = ${JSON.stringify(scanned)};\\n`;\n\t\t},\n\t};\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@openpolicy/vite-auto-collect",
|
|
3
|
+
"version": "0.0.18",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Vite plugin that scans source files for @openpolicy/sdk collecting() calls and populates autoCollected() at build time",
|
|
6
|
+
"license": "GPL-3.0-only",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/jamiedavenport/openpolicy",
|
|
10
|
+
"directory": "packages/vite-auto-collect"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"vite-plugin"
|
|
14
|
+
],
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"import": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"dev": "rolldown --watch -c",
|
|
27
|
+
"build": "rolldown -c",
|
|
28
|
+
"check-types": "tsc --noEmit",
|
|
29
|
+
"test": "bun test"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"vite": ">=4.0.0"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"oxc-parser": "^0.124.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@openpolicy/sdk": "workspace:*",
|
|
39
|
+
"@openpolicy/tooling": "workspace:*",
|
|
40
|
+
"vite": "catalog:"
|
|
41
|
+
}
|
|
42
|
+
}
|