usewagen 0.0.0 → 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 +31 -0
- package/client.d.ts +5 -0
- package/dist/builtins-DiCjLgsG.mjs +15 -0
- package/dist/create-storage-DESmB4bY.d.mts +38 -0
- package/dist/index.d.mts +43 -0
- package/dist/index.mjs +4 -0
- package/dist/options-BKybciek.d.mts +7 -0
- package/dist/parser-B6hfbAwA.mjs +2 -0
- package/dist/parsers-CA8aZuF-.mjs +156 -0
- package/dist/router/index.d.mts +40 -0
- package/dist/router/index.mjs +179 -0
- package/dist/storage/index.d.mts +43 -0
- package/dist/storage/index.mjs +84 -0
- package/dist/types-CNt2lcP1.d.mts +60 -0
- package/dist/types-CuwASQTi.d.mts +24 -0
- package/dist/utils-B-885gDp.mjs +76 -0
- package/dist/vite/index.d.mts +19 -0
- package/dist/vite/index.mjs +230 -0
- package/dist/wagen-C9_CPUdF.mjs +276 -0
- package/package.json +76 -3
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { _ as warnDev, h as ErrorCodes } from "../parsers-CA8aZuF-.mjs";
|
|
2
|
+
import { a as createLocalStorage, c as createStorage, o as createMemoryStorage, r as getActiveWagen, s as createSessionStorage } from "../wagen-C9_CPUdF.mjs";
|
|
3
|
+
import { i as toValueDeep, n as serializeValue, r as resolveParser, t as parseValue } from "../utils-B-885gDp.mjs";
|
|
4
|
+
import { computed, customRef, getCurrentScope, onWatcherCleanup, toValue, watch } from "vue";
|
|
5
|
+
//#region src/storage/define-storage-state.ts
|
|
6
|
+
function defineStorageState(options) {
|
|
7
|
+
const { key } = options;
|
|
8
|
+
const clearOnDefault = options.clearOnDefault ?? true;
|
|
9
|
+
const parser = resolveParser(options.parser);
|
|
10
|
+
function storage() {
|
|
11
|
+
const source = options.storage;
|
|
12
|
+
if (source && typeof source !== "string") return source;
|
|
13
|
+
const { storage } = getActiveWagen();
|
|
14
|
+
return source ? storage[source] : storage.default;
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
key,
|
|
18
|
+
get storage() {
|
|
19
|
+
return storage();
|
|
20
|
+
},
|
|
21
|
+
get: () => parseValue(parser, storage().getItem(key)),
|
|
22
|
+
set: (next) => {
|
|
23
|
+
const serialized = serializeValue(parser, clearOnDefault, next);
|
|
24
|
+
if (serialized === null) storage().removeItem(key);
|
|
25
|
+
else storage().setItem(key, serialized);
|
|
26
|
+
},
|
|
27
|
+
remove: () => storage().removeItem(key),
|
|
28
|
+
subscribe: (listener) => storage().subscribe(key, listener)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/storage/use-storage.ts
|
|
33
|
+
function isStorageState(input) {
|
|
34
|
+
return typeof input === "object" && input !== null && typeof input.get === "function";
|
|
35
|
+
}
|
|
36
|
+
function withResolvedStorage(options, wagen) {
|
|
37
|
+
const source = options.storage;
|
|
38
|
+
if (source && typeof source !== "string") return options;
|
|
39
|
+
return {
|
|
40
|
+
...options,
|
|
41
|
+
storage: source ? wagen.storage[source] : wagen.storage.default
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function useStorageState(input) {
|
|
45
|
+
if (isStorageState(input)) return computed(() => input);
|
|
46
|
+
const wagen = getActiveWagen();
|
|
47
|
+
return computed(() => defineStorageState(withResolvedStorage(toValueDeep(input), wagen)));
|
|
48
|
+
}
|
|
49
|
+
function useStorage(input) {
|
|
50
|
+
if (!getCurrentScope()) warnDev(ErrorCodes.NO_EFFECT_SCOPE, "useStorage");
|
|
51
|
+
const state = useStorageState(input);
|
|
52
|
+
let notify;
|
|
53
|
+
const value = customRef((track, trigger) => {
|
|
54
|
+
notify = trigger;
|
|
55
|
+
return {
|
|
56
|
+
get: () => {
|
|
57
|
+
track();
|
|
58
|
+
return state.value.get();
|
|
59
|
+
},
|
|
60
|
+
set: (next) => state.value.set(next)
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
watch(state, (current) => {
|
|
64
|
+
onWatcherCleanup(current.subscribe(notify));
|
|
65
|
+
}, {
|
|
66
|
+
immediate: true,
|
|
67
|
+
flush: "sync"
|
|
68
|
+
});
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
function useLocalStorage(options) {
|
|
72
|
+
return useStorage(() => ({
|
|
73
|
+
...toValue(options),
|
|
74
|
+
storage: "local"
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
function useSessionStorage(options) {
|
|
78
|
+
return useStorage(() => ({
|
|
79
|
+
...toValue(options),
|
|
80
|
+
storage: "session"
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
export { createLocalStorage, createMemoryStorage, createSessionStorage, createStorage, defineStorageState, useLocalStorage, useSessionStorage, useStorage };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
//#region src/parser/parsers.d.ts
|
|
2
|
+
type ParserOptions<T> = {
|
|
3
|
+
parse: (raw: string) => T | null;
|
|
4
|
+
serialize?: (value: T) => string;
|
|
5
|
+
};
|
|
6
|
+
type DefaultValue<T> = T | (() => T);
|
|
7
|
+
type Parser<T> = {
|
|
8
|
+
parse: (raw: string) => T | null;
|
|
9
|
+
serialize: (value: T) => string;
|
|
10
|
+
withDefault: (value: DefaultValue<T>) => ParserWithDefault<T>;
|
|
11
|
+
};
|
|
12
|
+
type ParserWithDefault<T> = Parser<T> & {
|
|
13
|
+
readonly defaultValue: DefaultValue<T>;
|
|
14
|
+
};
|
|
15
|
+
declare function tryParse<I, R>(fn: (input: I) => R, input: I): R | null;
|
|
16
|
+
declare function defineParser<T>(options: ParserOptions<T>): Parser<T>;
|
|
17
|
+
declare function unwrapDefault<T>(value: DefaultValue<T>): T;
|
|
18
|
+
declare const parseAsString: Parser<string>;
|
|
19
|
+
declare const parseAsInteger: Parser<number>;
|
|
20
|
+
declare const parseAsFloat: Parser<number>;
|
|
21
|
+
declare const parseAsIndex: Parser<number>;
|
|
22
|
+
declare const parseAsBoolean: Parser<boolean>;
|
|
23
|
+
declare function parseAsStringLiteral<const T extends readonly string[]>(values: T): Parser<T[number]>;
|
|
24
|
+
declare function parseAsNumberLiteral<const T extends readonly number[]>(values: T): Parser<T[number]>;
|
|
25
|
+
declare function parseAsStringEnum<T extends string>(values: T[]): Parser<T>;
|
|
26
|
+
declare const parseAsDate: Parser<Date> & {
|
|
27
|
+
iso: () => Parser<Date>;
|
|
28
|
+
timestamp: () => Parser<Date>;
|
|
29
|
+
};
|
|
30
|
+
declare function parseAsArrayOf<T>(itemParser: Parser<T>, separator?: string): Parser<T[]>;
|
|
31
|
+
declare function parseAsJson<T>(): Parser<T>;
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/parser/types.d.ts
|
|
34
|
+
type BuiltinParsers = {
|
|
35
|
+
parseAsString: Parser<string>;
|
|
36
|
+
parseAsInteger: Parser<number>;
|
|
37
|
+
parseAsFloat: Parser<number>;
|
|
38
|
+
parseAsIndex: Parser<number>;
|
|
39
|
+
parseAsBoolean: Parser<boolean>;
|
|
40
|
+
parseAsDate: Parser<Date>;
|
|
41
|
+
};
|
|
42
|
+
interface CustomParsers {}
|
|
43
|
+
type KnownParsers = BuiltinParsers & Omit<CustomParsers, keyof BuiltinParsers>;
|
|
44
|
+
type InferParserValue<P> = P extends Parser<infer T> ? T : never;
|
|
45
|
+
type NamedParserRef = { [K in keyof KnownParsers]: {
|
|
46
|
+
name: K;
|
|
47
|
+
defaultValue?: DefaultValue<InferParserValue<KnownParsers[K]>>;
|
|
48
|
+
}; }[keyof KnownParsers];
|
|
49
|
+
type ParserInput = (Parser<any> & {
|
|
50
|
+
name?: never;
|
|
51
|
+
}) | NamedParserRef;
|
|
52
|
+
type InferInputValue<P> = [P] extends [undefined] ? string | null : P extends ParserWithDefault<infer T> ? T : P extends Parser<infer T> ? T | null : P extends {
|
|
53
|
+
name: infer K;
|
|
54
|
+
defaultValue: any;
|
|
55
|
+
} ? K extends keyof KnownParsers ? InferParserValue<KnownParsers[K]> : string : P extends {
|
|
56
|
+
name: infer K;
|
|
57
|
+
} ? K extends keyof KnownParsers ? InferParserValue<KnownParsers[K]> | null : string | null : string | null;
|
|
58
|
+
type InferInputWritable<P> = InferInputValue<P> | null | undefined;
|
|
59
|
+
//#endregion
|
|
60
|
+
export { parseAsStringLiteral as C, parseAsStringEnum as S, unwrapDefault as T, parseAsIndex as _, InferParserValue as a, parseAsNumberLiteral as b, DefaultValue as c, ParserWithDefault as d, defineParser as f, parseAsFloat as g, parseAsDate as h, InferInputWritable as i, Parser as l, parseAsBoolean as m, CustomParsers as n, KnownParsers as o, parseAsArrayOf as p, InferInputValue as r, ParserInput as s, BuiltinParsers as t, ParserOptions as u, parseAsInteger as v, tryParse as w, parseAsString as x, parseAsJson as y };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { c as DefaultValue, s as ParserInput } from "./types-CNt2lcP1.mjs";
|
|
2
|
+
//#region src/parser/resolve.d.ts
|
|
3
|
+
type ResolvedParser<T> = {
|
|
4
|
+
parse: (raw: string) => T | null;
|
|
5
|
+
serialize: (value: T) => string;
|
|
6
|
+
defaultValue?: DefaultValue<T>;
|
|
7
|
+
};
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/router/types.d.ts
|
|
10
|
+
type RouteStateSource = 'params' | 'query';
|
|
11
|
+
type HistoryMode = 'push' | 'replace';
|
|
12
|
+
interface RouteStateOptions {
|
|
13
|
+
key: string;
|
|
14
|
+
parser?: ParserInput;
|
|
15
|
+
urlKey?: string;
|
|
16
|
+
source?: RouteStateSource;
|
|
17
|
+
history?: HistoryMode;
|
|
18
|
+
clearOnDefault?: boolean;
|
|
19
|
+
}
|
|
20
|
+
type ResolvedRouteStateOptions = Omit<Required<RouteStateOptions>, 'parser'> & {
|
|
21
|
+
parser: ResolvedParser<any>;
|
|
22
|
+
};
|
|
23
|
+
//#endregion
|
|
24
|
+
export { RouteStateSource as i, ResolvedRouteStateOptions as n, RouteStateOptions as r, HistoryMode as t };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { g as warn, h as ErrorCodes, m as unwrapDefault, u as parseAsString } from "./parsers-CA8aZuF-.mjs";
|
|
2
|
+
import { r as getActiveWagen } from "./wagen-C9_CPUdF.mjs";
|
|
3
|
+
import { n as isBuiltinParserName, t as builtinParsers } from "./builtins-DiCjLgsG.mjs";
|
|
4
|
+
import { toValue } from "vue";
|
|
5
|
+
//#region src/options.ts
|
|
6
|
+
function toValueDeep(input) {
|
|
7
|
+
const source = toValue(input);
|
|
8
|
+
const result = {};
|
|
9
|
+
for (const key in source) result[key] = toValue(source[key]);
|
|
10
|
+
return result;
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/parser/resolve.ts
|
|
14
|
+
function getParser(name) {
|
|
15
|
+
if (isBuiltinParserName(name)) return builtinParsers[name];
|
|
16
|
+
const { parsers } = getActiveWagen();
|
|
17
|
+
return Object.hasOwn(parsers, name) ? parsers[name] : void 0;
|
|
18
|
+
}
|
|
19
|
+
function fromParser(parser) {
|
|
20
|
+
const resolved = {
|
|
21
|
+
parse: parser.parse,
|
|
22
|
+
serialize: parser.serialize
|
|
23
|
+
};
|
|
24
|
+
if ("defaultValue" in parser) resolved.defaultValue = parser.defaultValue;
|
|
25
|
+
return resolved;
|
|
26
|
+
}
|
|
27
|
+
function fromName(name, override) {
|
|
28
|
+
let warned = false;
|
|
29
|
+
function get() {
|
|
30
|
+
const found = getParser(name);
|
|
31
|
+
if (found) return found;
|
|
32
|
+
if (!warned) {
|
|
33
|
+
warned = true;
|
|
34
|
+
warn(ErrorCodes.UNKNOWN_PARSER_NAME, name);
|
|
35
|
+
}
|
|
36
|
+
return parseAsString;
|
|
37
|
+
}
|
|
38
|
+
const base = {
|
|
39
|
+
parse: (raw) => get().parse(raw),
|
|
40
|
+
serialize: (value) => get().serialize(value)
|
|
41
|
+
};
|
|
42
|
+
if (override) return {
|
|
43
|
+
...base,
|
|
44
|
+
defaultValue: override.defaultValue
|
|
45
|
+
};
|
|
46
|
+
return {
|
|
47
|
+
...base,
|
|
48
|
+
get defaultValue() {
|
|
49
|
+
return get().defaultValue;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function resolveParser(input = { name: "parseAsString" }) {
|
|
54
|
+
if ("parse" in input) return fromParser(input);
|
|
55
|
+
const override = "defaultValue" in input ? { defaultValue: input.defaultValue } : void 0;
|
|
56
|
+
return fromName(input.name, override);
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/parser/utils.ts
|
|
60
|
+
function parseValue(parser, raw) {
|
|
61
|
+
const value = Array.isArray(raw) ? raw.find((v) => v != null) : raw;
|
|
62
|
+
if (value == null) return parser.defaultValue !== void 0 ? unwrapDefault(parser.defaultValue) : null;
|
|
63
|
+
const parsed = parser.parse(value);
|
|
64
|
+
if (parsed !== null) return parsed;
|
|
65
|
+
return parser.defaultValue !== void 0 ? unwrapDefault(parser.defaultValue) : null;
|
|
66
|
+
}
|
|
67
|
+
function serializeValue(parser, clearOnDefault, next) {
|
|
68
|
+
if (next == null) return null;
|
|
69
|
+
const serialized = parser.serialize(next);
|
|
70
|
+
if (clearOnDefault && parser.defaultValue !== void 0) {
|
|
71
|
+
if (serialized === parser.serialize(unwrapDefault(parser.defaultValue))) return null;
|
|
72
|
+
}
|
|
73
|
+
return serialized;
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
export { toValueDeep as i, serializeValue as n, resolveParser as r, parseValue as t };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Plugin } from "vite";
|
|
2
|
+
//#region src/vite/index.d.ts
|
|
3
|
+
declare const PARSERS_ID = "virtual:usewagen/parsers";
|
|
4
|
+
declare const RESOLVED_PARSERS_ID = "\0virtual:usewagen/parsers";
|
|
5
|
+
interface WagenPluginOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Directory or directories to scan for parser files.
|
|
8
|
+
* @default 'src/parsers'
|
|
9
|
+
*/
|
|
10
|
+
dirs?: string | string[];
|
|
11
|
+
/**
|
|
12
|
+
* Output path for the generated `.d.ts` file, or `false` to disable.
|
|
13
|
+
* @default 'usewagen.d.ts'
|
|
14
|
+
*/
|
|
15
|
+
dts?: string | false;
|
|
16
|
+
}
|
|
17
|
+
declare function usewagen(options?: WagenPluginOptions): Plugin;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { PARSERS_ID, RESOLVED_PARSERS_ID, WagenPluginOptions, usewagen as default, usewagen };
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { g as warn, h as ErrorCodes } from "../parsers-CA8aZuF-.mjs";
|
|
2
|
+
import { n as isBuiltinParserName } from "../builtins-DiCjLgsG.mjs";
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
|
+
//#region src/vite/generate.ts
|
|
6
|
+
const BANNER = "// Auto-generated by usewagen/vite — do not edit";
|
|
7
|
+
function toPosix(path) {
|
|
8
|
+
return path.split(sep).join("/");
|
|
9
|
+
}
|
|
10
|
+
function toSpecifier(from, file) {
|
|
11
|
+
const target = relative(from, file);
|
|
12
|
+
const specifier = toPosix(join(dirname(target), basename(target, extname(target))));
|
|
13
|
+
return specifier.startsWith(".") ? specifier : `./${specifier}`;
|
|
14
|
+
}
|
|
15
|
+
function generateModule(entries) {
|
|
16
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
17
|
+
for (const { name, file } of entries) {
|
|
18
|
+
const names = byFile.get(file) ?? [];
|
|
19
|
+
names.push(name);
|
|
20
|
+
byFile.set(file, names);
|
|
21
|
+
}
|
|
22
|
+
const lines = [BANNER];
|
|
23
|
+
for (const [file, names] of byFile) lines.push(`import { ${names.join(", ")} } from ${JSON.stringify(toPosix(file))}`);
|
|
24
|
+
lines.push("");
|
|
25
|
+
if (entries.length === 0) {
|
|
26
|
+
lines.push("export const parsers = {}", "");
|
|
27
|
+
return lines.join("\n");
|
|
28
|
+
}
|
|
29
|
+
lines.push("export const parsers = {");
|
|
30
|
+
for (const { name } of entries) lines.push(` ${name},`);
|
|
31
|
+
lines.push("}", "");
|
|
32
|
+
return lines.join("\n");
|
|
33
|
+
}
|
|
34
|
+
function generateDeclaration(entries, dtsPath) {
|
|
35
|
+
const dtsDir = dirname(dtsPath);
|
|
36
|
+
const lines = [
|
|
37
|
+
BANNER,
|
|
38
|
+
"/// <reference types=\"usewagen/client\" />",
|
|
39
|
+
"export {}",
|
|
40
|
+
"",
|
|
41
|
+
"declare module 'usewagen' {",
|
|
42
|
+
" interface CustomParsers {"
|
|
43
|
+
];
|
|
44
|
+
for (const { name, file } of entries) lines.push(` ${name}: typeof import('${toSpecifier(dtsDir, file)}')['${name}']`);
|
|
45
|
+
lines.push(" }", "}", "");
|
|
46
|
+
return lines.join("\n");
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/vite/strip.ts
|
|
50
|
+
const DIVISION_PRECEDING = /[\w$)\]]/;
|
|
51
|
+
function stripNonCode(source) {
|
|
52
|
+
let output = "";
|
|
53
|
+
let index = 0;
|
|
54
|
+
let previous = "";
|
|
55
|
+
const push = (char) => {
|
|
56
|
+
output += char;
|
|
57
|
+
if (!/\s/.test(char)) previous = char;
|
|
58
|
+
};
|
|
59
|
+
while (index < source.length) {
|
|
60
|
+
const char = source[index];
|
|
61
|
+
const next = source[index + 1];
|
|
62
|
+
if (char === "/" && next === "/") {
|
|
63
|
+
while (index < source.length && source[index] !== "\n") index++;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (char === "/" && next === "*") {
|
|
67
|
+
index += 2;
|
|
68
|
+
while (index < source.length && !(source[index] === "*" && source[index + 1] === "/")) {
|
|
69
|
+
if (source[index] === "\n") output += "\n";
|
|
70
|
+
index++;
|
|
71
|
+
}
|
|
72
|
+
index += 2;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (char === "\"" || char === "'" || char === "`") {
|
|
76
|
+
index++;
|
|
77
|
+
while (index < source.length && source[index] !== char) {
|
|
78
|
+
if (source[index] === "\\") index++;
|
|
79
|
+
index++;
|
|
80
|
+
}
|
|
81
|
+
index++;
|
|
82
|
+
push(char);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (char === "/" && !DIVISION_PRECEDING.test(previous)) {
|
|
86
|
+
index++;
|
|
87
|
+
let inClass = false;
|
|
88
|
+
while (index < source.length && source[index] !== "\n") {
|
|
89
|
+
const current = source[index];
|
|
90
|
+
if (current === "\\") {
|
|
91
|
+
index += 2;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (current === "[") inClass = true;
|
|
95
|
+
else if (current === "]") inClass = false;
|
|
96
|
+
else if (current === "/" && !inClass) break;
|
|
97
|
+
index++;
|
|
98
|
+
}
|
|
99
|
+
index++;
|
|
100
|
+
previous = "/";
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
push(char);
|
|
104
|
+
index++;
|
|
105
|
+
}
|
|
106
|
+
return output;
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region src/vite/scan.ts
|
|
110
|
+
const MODULE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
111
|
+
".js",
|
|
112
|
+
".mjs",
|
|
113
|
+
".ts",
|
|
114
|
+
".mts"
|
|
115
|
+
]);
|
|
116
|
+
const DECLARATION_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".mts"]);
|
|
117
|
+
const PARSER_EXPORT = /export\s+const\s+(parseAs\w+)/g;
|
|
118
|
+
function isDeclarationFile(name, extension) {
|
|
119
|
+
return DECLARATION_EXTENSIONS.has(extension) && basename(name, extension).endsWith(".d");
|
|
120
|
+
}
|
|
121
|
+
function isParserFile(name) {
|
|
122
|
+
const extension = extname(name);
|
|
123
|
+
return MODULE_EXTENSIONS.has(extension) && !isDeclarationFile(name, extension);
|
|
124
|
+
}
|
|
125
|
+
function extractNames(file) {
|
|
126
|
+
const source = stripNonCode(readFileSync(file, "utf-8"));
|
|
127
|
+
const names = [];
|
|
128
|
+
PARSER_EXPORT.lastIndex = 0;
|
|
129
|
+
let match;
|
|
130
|
+
while ((match = PARSER_EXPORT.exec(source)) !== null) {
|
|
131
|
+
const name = match[1];
|
|
132
|
+
if (isBuiltinParserName(name)) {
|
|
133
|
+
warn(ErrorCodes.RESERVED_PARSER_NAME, name, file);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
names.push(name);
|
|
137
|
+
}
|
|
138
|
+
return names;
|
|
139
|
+
}
|
|
140
|
+
function walk(dir, entries) {
|
|
141
|
+
if (!existsSync(dir)) return;
|
|
142
|
+
for (const item of readdirSync(dir, { withFileTypes: true })) {
|
|
143
|
+
if (item.isDirectory()) {
|
|
144
|
+
walk(resolve(dir, item.name), entries);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (!isParserFile(item.name)) continue;
|
|
148
|
+
const file = resolve(dir, item.name);
|
|
149
|
+
for (const name of extractNames(file)) entries.push({
|
|
150
|
+
name,
|
|
151
|
+
file
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function dedupe(entries) {
|
|
156
|
+
const byName = /* @__PURE__ */ new Map();
|
|
157
|
+
for (const entry of entries) {
|
|
158
|
+
const previous = byName.get(entry.name);
|
|
159
|
+
if (previous) warn(ErrorCodes.DUPLICATE_PARSER_NAME, entry.name, previous.file, entry.file);
|
|
160
|
+
byName.set(entry.name, entry);
|
|
161
|
+
}
|
|
162
|
+
return [...byName.values()];
|
|
163
|
+
}
|
|
164
|
+
function scanParsers(dirs) {
|
|
165
|
+
const entries = [];
|
|
166
|
+
for (const dir of dirs) walk(dir, entries);
|
|
167
|
+
return dedupe(entries);
|
|
168
|
+
}
|
|
169
|
+
//#endregion
|
|
170
|
+
//#region src/vite/index.ts
|
|
171
|
+
const PARSERS_ID = "virtual:usewagen/parsers";
|
|
172
|
+
const RESOLVED_PARSERS_ID = `\0${PARSERS_ID}`;
|
|
173
|
+
function contains(dir, path) {
|
|
174
|
+
const target = relative(dir, path);
|
|
175
|
+
return !isAbsolute(target) && !target.startsWith(`..${sep}`);
|
|
176
|
+
}
|
|
177
|
+
function usewagen(options = {}) {
|
|
178
|
+
const { dirs: rawDirs = "src/parsers", dts = "usewagen.d.ts" } = options;
|
|
179
|
+
let dirs = [];
|
|
180
|
+
let dtsPath = "";
|
|
181
|
+
let entries = null;
|
|
182
|
+
function scan() {
|
|
183
|
+
entries ??= scanParsers(dirs);
|
|
184
|
+
return entries;
|
|
185
|
+
}
|
|
186
|
+
function writeDeclaration() {
|
|
187
|
+
if (dts === false) return;
|
|
188
|
+
const content = generateDeclaration(scan(), dtsPath);
|
|
189
|
+
const dir = dirname(dtsPath);
|
|
190
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
191
|
+
if ((existsSync(dtsPath) ? readFileSync(dtsPath, "utf-8") : "") !== content) writeFileSync(dtsPath, content, "utf-8");
|
|
192
|
+
}
|
|
193
|
+
function isScanned(path) {
|
|
194
|
+
return dirs.some((dir) => contains(dir, path)) && isParserFile(basename(path));
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
name: "usewagen",
|
|
198
|
+
configResolved(config) {
|
|
199
|
+
dirs = (Array.isArray(rawDirs) ? rawDirs : [rawDirs]).map((dir) => resolve(config.root, dir));
|
|
200
|
+
if (dts !== false) dtsPath = resolve(config.root, dts);
|
|
201
|
+
entries = null;
|
|
202
|
+
writeDeclaration();
|
|
203
|
+
},
|
|
204
|
+
resolveId(id) {
|
|
205
|
+
if (id === "virtual:usewagen/parsers") return RESOLVED_PARSERS_ID;
|
|
206
|
+
},
|
|
207
|
+
load(id) {
|
|
208
|
+
if (id !== RESOLVED_PARSERS_ID) return;
|
|
209
|
+
const found = scan();
|
|
210
|
+
for (const dir of dirs) this.addWatchFile(dir);
|
|
211
|
+
for (const { file } of found) this.addWatchFile(file);
|
|
212
|
+
return generateModule(found);
|
|
213
|
+
},
|
|
214
|
+
configureServer(server) {
|
|
215
|
+
for (const dir of dirs) server.watcher.add(dir);
|
|
216
|
+
server.watcher.on("all", (event, path) => {
|
|
217
|
+
if (event !== "add" && event !== "unlink" && event !== "change") return;
|
|
218
|
+
if (!isScanned(path)) return;
|
|
219
|
+
entries = null;
|
|
220
|
+
writeDeclaration();
|
|
221
|
+
const mod = server.moduleGraph.getModuleById(RESOLVED_PARSERS_ID);
|
|
222
|
+
if (!mod) return;
|
|
223
|
+
server.moduleGraph.invalidateModule(mod);
|
|
224
|
+
server.ws.send({ type: "full-reload" });
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
//#endregion
|
|
230
|
+
export { PARSERS_ID, RESOLVED_PARSERS_ID, usewagen as default, usewagen };
|