usewagen 0.0.0 → 0.2.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 +46 -0
- package/dist/index.mjs +4 -0
- package/dist/parser-B6hfbAwA.mjs +2 -0
- package/dist/parsers-CA8aZuF-.mjs +156 -0
- package/dist/router/index.d.mts +42 -0
- package/dist/router/index.mjs +273 -0
- package/dist/storage/index.d.mts +37 -0
- package/dist/storage/index.mjs +115 -0
- package/dist/types-JtRmT3qo.d.mts +72 -0
- package/dist/types-fVW6rBJ3.d.mts +25 -0
- package/dist/utils-EL2EiRpH.mjs +79 -0
- package/dist/vite/index.d.mts +19 -0
- package/dist/vite/index.mjs +230 -0
- package/dist/wagen-C2p8oQgu.mjs +278 -0
- package/package.json +76 -3
|
@@ -0,0 +1,115 @@
|
|
|
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-C2p8oQgu.mjs";
|
|
3
|
+
import { a as toValueDeep, i as resolveParser, n as parseValue, r as serializeValue } from "../utils-EL2EiRpH.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
|
+
let cache = null;
|
|
11
|
+
function resolved() {
|
|
12
|
+
const source = options.storage;
|
|
13
|
+
const mode = options.mode;
|
|
14
|
+
if (mode !== void 0 && source !== void 0 && typeof source !== "string") return {
|
|
15
|
+
instance: source,
|
|
16
|
+
optimistic: mode === "optimistic"
|
|
17
|
+
};
|
|
18
|
+
const wagen = getActiveWagen();
|
|
19
|
+
return {
|
|
20
|
+
instance: source === void 0 ? wagen.storage.default : typeof source === "string" ? wagen.storage[source] : source,
|
|
21
|
+
optimistic: (mode ?? wagen.storage.mode) === "optimistic"
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function write(serialized) {
|
|
25
|
+
const { instance, optimistic } = resolved();
|
|
26
|
+
if (optimistic) cache = {
|
|
27
|
+
seen: instance.getItem(key),
|
|
28
|
+
raw: serialized
|
|
29
|
+
};
|
|
30
|
+
if (serialized === null) instance.removeItem(key);
|
|
31
|
+
else instance.setItem(key, serialized);
|
|
32
|
+
if (optimistic) cache = {
|
|
33
|
+
seen: instance.getItem(key),
|
|
34
|
+
raw: serialized
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
key,
|
|
39
|
+
get storage() {
|
|
40
|
+
return resolved().instance;
|
|
41
|
+
},
|
|
42
|
+
get: () => {
|
|
43
|
+
const { instance, optimistic } = resolved();
|
|
44
|
+
const current = instance.getItem(key);
|
|
45
|
+
if (!optimistic) {
|
|
46
|
+
cache = null;
|
|
47
|
+
return parseValue(parser, current);
|
|
48
|
+
}
|
|
49
|
+
if (cache && cache.seen === current) return parseValue(parser, cache.raw);
|
|
50
|
+
cache = {
|
|
51
|
+
seen: current,
|
|
52
|
+
raw: current
|
|
53
|
+
};
|
|
54
|
+
return parseValue(parser, current);
|
|
55
|
+
},
|
|
56
|
+
set: (next) => write(serializeValue(parser, clearOnDefault, next)),
|
|
57
|
+
remove: () => write(null),
|
|
58
|
+
subscribe: (listener) => resolved().instance.subscribe(key, listener)
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/storage/use-storage.ts
|
|
63
|
+
function isStorageState(input) {
|
|
64
|
+
return typeof input === "object" && input !== null && typeof input.get === "function";
|
|
65
|
+
}
|
|
66
|
+
function withWagenDefaults(options, wagen) {
|
|
67
|
+
const source = options.storage;
|
|
68
|
+
const storage = source === void 0 ? wagen.storage.default : typeof source === "string" ? wagen.storage[source] : source;
|
|
69
|
+
return {
|
|
70
|
+
...options,
|
|
71
|
+
storage,
|
|
72
|
+
mode: options.mode ?? wagen.storage.mode
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function useStorageState(input) {
|
|
76
|
+
if (isStorageState(input)) return computed(() => input);
|
|
77
|
+
const wagen = getActiveWagen();
|
|
78
|
+
return computed(() => defineStorageState(withWagenDefaults(toValueDeep(input), wagen)));
|
|
79
|
+
}
|
|
80
|
+
function useStorage(input) {
|
|
81
|
+
if (!getCurrentScope()) warnDev(ErrorCodes.NO_EFFECT_SCOPE, "useStorage");
|
|
82
|
+
const state = useStorageState(input);
|
|
83
|
+
let notify;
|
|
84
|
+
const value = customRef((track, trigger) => {
|
|
85
|
+
notify = trigger;
|
|
86
|
+
return {
|
|
87
|
+
get: () => {
|
|
88
|
+
track();
|
|
89
|
+
return state.value.get();
|
|
90
|
+
},
|
|
91
|
+
set: (next) => state.value.set(next)
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
watch(state, (current) => {
|
|
95
|
+
onWatcherCleanup(current.subscribe(notify));
|
|
96
|
+
}, {
|
|
97
|
+
immediate: true,
|
|
98
|
+
flush: "sync"
|
|
99
|
+
});
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
function useLocalStorage(options) {
|
|
103
|
+
return useStorage(() => ({
|
|
104
|
+
...toValue(options),
|
|
105
|
+
storage: "local"
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
function useSessionStorage(options) {
|
|
109
|
+
return useStorage(() => ({
|
|
110
|
+
...toValue(options),
|
|
111
|
+
storage: "session"
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
//#endregion
|
|
115
|
+
export { createLocalStorage, createMemoryStorage, createSessionStorage, createStorage, defineStorageState, useLocalStorage, useSessionStorage, useStorage };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { MaybeRefOrGetter, Ref } from "vue";
|
|
2
|
+
//#region src/parser/parsers.d.ts
|
|
3
|
+
type ParserOptions<T> = {
|
|
4
|
+
parse: (raw: string) => T | null;
|
|
5
|
+
serialize?: (value: T) => string;
|
|
6
|
+
};
|
|
7
|
+
type DefaultValue<T> = T | (() => T);
|
|
8
|
+
type Parser<T> = {
|
|
9
|
+
parse: (raw: string) => T | null;
|
|
10
|
+
serialize: (value: T) => string;
|
|
11
|
+
withDefault: (value: DefaultValue<T>) => ParserWithDefault<T>;
|
|
12
|
+
};
|
|
13
|
+
type ParserWithDefault<T> = Parser<T> & {
|
|
14
|
+
readonly defaultValue: DefaultValue<T>;
|
|
15
|
+
};
|
|
16
|
+
declare function tryParse<I, R>(fn: (input: I) => R, input: I): R | null;
|
|
17
|
+
declare function defineParser<T>(options: ParserOptions<T>): Parser<T>;
|
|
18
|
+
declare function unwrapDefault<T>(value: DefaultValue<T>): T;
|
|
19
|
+
declare const parseAsString: Parser<string>;
|
|
20
|
+
declare const parseAsInteger: Parser<number>;
|
|
21
|
+
declare const parseAsFloat: Parser<number>;
|
|
22
|
+
declare const parseAsIndex: Parser<number>;
|
|
23
|
+
declare const parseAsBoolean: Parser<boolean>;
|
|
24
|
+
declare function parseAsStringLiteral<const T extends readonly string[]>(values: T): Parser<T[number]>;
|
|
25
|
+
declare function parseAsNumberLiteral<const T extends readonly number[]>(values: T): Parser<T[number]>;
|
|
26
|
+
declare function parseAsStringEnum<T extends string>(values: T[]): Parser<T>;
|
|
27
|
+
declare const parseAsDate: Parser<Date> & {
|
|
28
|
+
iso: () => Parser<Date>;
|
|
29
|
+
timestamp: () => Parser<Date>;
|
|
30
|
+
};
|
|
31
|
+
declare function parseAsArrayOf<T>(itemParser: Parser<T>, separator?: string): Parser<T[]>;
|
|
32
|
+
declare function parseAsJson<T>(): Parser<T>;
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/parser/types.d.ts
|
|
35
|
+
type BuiltinParsers = {
|
|
36
|
+
parseAsString: Parser<string>;
|
|
37
|
+
parseAsInteger: Parser<number>;
|
|
38
|
+
parseAsFloat: Parser<number>;
|
|
39
|
+
parseAsIndex: Parser<number>;
|
|
40
|
+
parseAsBoolean: Parser<boolean>;
|
|
41
|
+
parseAsDate: Parser<Date>;
|
|
42
|
+
};
|
|
43
|
+
interface CustomParsers {}
|
|
44
|
+
type KnownParsers = BuiltinParsers & Omit<CustomParsers, keyof BuiltinParsers>;
|
|
45
|
+
type InferParserValue<P> = P extends Parser<infer T> ? T : never;
|
|
46
|
+
type NamedParserRef = { [K in keyof KnownParsers]: {
|
|
47
|
+
name: K;
|
|
48
|
+
defaultValue?: DefaultValue<InferParserValue<KnownParsers[K]>>;
|
|
49
|
+
}; }[keyof KnownParsers];
|
|
50
|
+
type ParserInput = (Parser<any> & {
|
|
51
|
+
name?: never;
|
|
52
|
+
}) | NamedParserRef;
|
|
53
|
+
type UnwrapParser<P> = P extends Ref<infer U> ? U : P extends (() => infer U) ? U : P;
|
|
54
|
+
type WithParser<T, P extends ParserInput | undefined> = Omit<T, 'parser'> & {
|
|
55
|
+
parser?: P;
|
|
56
|
+
};
|
|
57
|
+
type InferInputValue<P> = ResolveInputValue<UnwrapParser<P>>;
|
|
58
|
+
type ResolveInputValue<P> = [P] extends [undefined] ? string | null : P extends ParserWithDefault<infer T> ? T : P extends Parser<infer T> ? T | null : P extends {
|
|
59
|
+
name: infer K;
|
|
60
|
+
defaultValue: any;
|
|
61
|
+
} ? K extends keyof KnownParsers ? InferParserValue<KnownParsers[K]> : string : P extends {
|
|
62
|
+
name: infer K;
|
|
63
|
+
} ? K extends keyof KnownParsers ? InferParserValue<KnownParsers[K]> | null : string | null : string | null;
|
|
64
|
+
type InferInputWritable<P> = InferInputValue<P> | null | undefined;
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region src/types.d.ts
|
|
67
|
+
type StateMode = 'optimistic' | 'source';
|
|
68
|
+
type MaybeRefsOrGetters<T> = { [K in keyof T]: MaybeRefOrGetter<T[K]>; };
|
|
69
|
+
type ReactiveFields<T, TStatic extends keyof T = never> = MaybeRefsOrGetters<Omit<T, TStatic>> & Pick<T, TStatic>;
|
|
70
|
+
type ReactiveOptions<T, TStatic extends keyof T = never> = MaybeRefOrGetter<ReactiveFields<T, TStatic>>;
|
|
71
|
+
//#endregion
|
|
72
|
+
export { unwrapDefault as A, parseAsInteger as C, parseAsStringEnum as D, parseAsString as E, parseAsStringLiteral as O, parseAsIndex as S, parseAsNumberLiteral as T, defineParser as _, CustomParsers as a, parseAsDate as b, InferParserValue as c, UnwrapParser as d, WithParser as f, ParserWithDefault as g, ParserOptions as h, BuiltinParsers as i, tryParse as k, KnownParsers as l, Parser as m, ReactiveOptions as n, InferInputValue as o, DefaultValue as p, StateMode as r, InferInputWritable as s, ReactiveFields as t, ParserInput as u, parseAsArrayOf as v, parseAsJson as w, parseAsFloat as x, parseAsBoolean as y };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { p as DefaultValue, r as StateMode, u as ParserInput } from "./types-JtRmT3qo.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
|
+
mode?: StateMode;
|
|
20
|
+
}
|
|
21
|
+
type ResolvedRouteStateOptions = Omit<Required<RouteStateOptions>, 'parser'> & {
|
|
22
|
+
parser: ResolvedParser<any>;
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { RouteStateSource as i, ResolvedRouteStateOptions as n, RouteStateOptions as r, HistoryMode as t };
|
|
@@ -0,0 +1,79 @@
|
|
|
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-C2p8oQgu.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 normalizeRaw(raw) {
|
|
61
|
+
return (Array.isArray(raw) ? raw.find((v) => v != null) : raw) ?? null;
|
|
62
|
+
}
|
|
63
|
+
function parseValue(parser, raw) {
|
|
64
|
+
const value = normalizeRaw(raw);
|
|
65
|
+
if (value === null) return parser.defaultValue !== void 0 ? unwrapDefault(parser.defaultValue) : null;
|
|
66
|
+
const parsed = parser.parse(value);
|
|
67
|
+
if (parsed !== null) return parsed;
|
|
68
|
+
return parser.defaultValue !== void 0 ? unwrapDefault(parser.defaultValue) : null;
|
|
69
|
+
}
|
|
70
|
+
function serializeValue(parser, clearOnDefault, next) {
|
|
71
|
+
if (next == null) return null;
|
|
72
|
+
const serialized = parser.serialize(next);
|
|
73
|
+
if (clearOnDefault && parser.defaultValue !== void 0) {
|
|
74
|
+
if (serialized === parser.serialize(unwrapDefault(parser.defaultValue))) return null;
|
|
75
|
+
}
|
|
76
|
+
return serialized;
|
|
77
|
+
}
|
|
78
|
+
//#endregion
|
|
79
|
+
export { toValueDeep as a, resolveParser as i, parseValue as n, serializeValue as r, normalizeRaw as t };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Plugin } from "vite";
|
|
2
|
+
//#region src/vite/index.d.ts
|
|
3
|
+
export declare const PARSERS_ID = "virtual:usewagen/parsers";
|
|
4
|
+
export declare const RESOLVED_PARSERS_ID = "\0virtual:usewagen/parsers";
|
|
5
|
+
export 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
|
+
export declare function usewagen(options?: WagenPluginOptions): Plugin;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { usewagen as default };
|
|
@@ -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 };
|