@mionjs/devtools 0.8.0-alpha.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/build/eslint/cjs/index.cjs +41 -0
- package/build/eslint/cjs/index.cjs.map +1 -0
- package/build/eslint/cjs/package.json +1 -0
- package/build/eslint/cjs/src/eslint/index.cjs +13 -0
- package/build/eslint/cjs/src/eslint/index.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/index.d.ts +3 -0
- package/build/eslint/cjs/src/eslint/rules/enforce-type-imports.cjs +121 -0
- package/build/eslint/cjs/src/eslint/rules/enforce-type-imports.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/enforce-type-imports.d.ts +7 -0
- package/build/eslint/cjs/src/eslint/rules/formatTypeNames.cjs +71 -0
- package/build/eslint/cjs/src/eslint/rules/formatTypeNames.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/formatTypeNames.d.ts +3 -0
- package/build/eslint/cjs/src/eslint/rules/no-mixed-union-properties.cjs +220 -0
- package/build/eslint/cjs/src/eslint/rules/no-mixed-union-properties.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-mixed-union-properties.d.ts +3 -0
- package/build/eslint/cjs/src/eslint/rules/no-type-imports.cjs +251 -0
- package/build/eslint/cjs/src/eslint/rules/no-type-imports.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-type-imports.d.ts +3 -0
- package/build/eslint/cjs/src/eslint/rules/no-typeof-runtype.cjs +72 -0
- package/build/eslint/cjs/src/eslint/rules/no-typeof-runtype.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-typeof-runtype.d.ts +3 -0
- package/build/eslint/cjs/src/eslint/rules/no-unreachable-union-types.cjs +220 -0
- package/build/eslint/cjs/src/eslint/rules/no-unreachable-union-types.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-unreachable-union-types.d.ts +3 -0
- package/build/eslint/cjs/src/eslint/rules/no-vite-client.cjs +71 -0
- package/build/eslint/cjs/src/eslint/rules/no-vite-client.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-vite-client.d.ts +4 -0
- package/build/eslint/cjs/src/eslint/rules/pure-functions.cjs +346 -0
- package/build/eslint/cjs/src/eslint/rules/pure-functions.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/pure-functions.d.ts +4 -0
- package/build/eslint/cjs/src/eslint/rules/strong-typed-routes.cjs +328 -0
- package/build/eslint/cjs/src/eslint/rules/strong-typed-routes.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/strong-typed-routes.d.ts +4 -0
- package/build/eslint/cjs/src/eslint/rules/type-formats-imports.cjs +52 -0
- package/build/eslint/cjs/src/eslint/rules/type-formats-imports.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/type-formats-imports.d.ts +3 -0
- package/build/eslint/cjs/src/pureFns/purityRules.cjs +67 -0
- package/build/eslint/cjs/src/pureFns/purityRules.cjs.map +1 -0
- package/build/eslint/cjs/src/pureFns/purityRules.d.ts +4 -0
- package/build/eslint/esm/index.js +42 -0
- package/build/eslint/esm/index.js.map +1 -0
- package/build/eslint/esm/src/eslint/index.d.ts +3 -0
- package/build/eslint/esm/src/eslint/index.js +14 -0
- package/build/eslint/esm/src/eslint/index.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/enforce-type-imports.d.ts +7 -0
- package/build/eslint/esm/src/eslint/rules/enforce-type-imports.js +122 -0
- package/build/eslint/esm/src/eslint/rules/enforce-type-imports.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/formatTypeNames.d.ts +3 -0
- package/build/eslint/esm/src/eslint/rules/formatTypeNames.js +71 -0
- package/build/eslint/esm/src/eslint/rules/formatTypeNames.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/no-mixed-union-properties.d.ts +3 -0
- package/build/eslint/esm/src/eslint/rules/no-mixed-union-properties.js +221 -0
- package/build/eslint/esm/src/eslint/rules/no-mixed-union-properties.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/no-type-imports.d.ts +3 -0
- package/build/eslint/esm/src/eslint/rules/no-type-imports.js +252 -0
- package/build/eslint/esm/src/eslint/rules/no-type-imports.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/no-typeof-runtype.d.ts +3 -0
- package/build/eslint/esm/src/eslint/rules/no-typeof-runtype.js +73 -0
- package/build/eslint/esm/src/eslint/rules/no-typeof-runtype.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/no-unreachable-union-types.d.ts +3 -0
- package/build/eslint/esm/src/eslint/rules/no-unreachable-union-types.js +221 -0
- package/build/eslint/esm/src/eslint/rules/no-unreachable-union-types.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/no-vite-client.d.ts +4 -0
- package/build/eslint/esm/src/eslint/rules/no-vite-client.js +72 -0
- package/build/eslint/esm/src/eslint/rules/no-vite-client.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/pure-functions.d.ts +4 -0
- package/build/eslint/esm/src/eslint/rules/pure-functions.js +347 -0
- package/build/eslint/esm/src/eslint/rules/pure-functions.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/strong-typed-routes.d.ts +4 -0
- package/build/eslint/esm/src/eslint/rules/strong-typed-routes.js +329 -0
- package/build/eslint/esm/src/eslint/rules/strong-typed-routes.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/type-formats-imports.d.ts +3 -0
- package/build/eslint/esm/src/eslint/rules/type-formats-imports.js +53 -0
- package/build/eslint/esm/src/eslint/rules/type-formats-imports.js.map +1 -0
- package/build/eslint/esm/src/pureFns/purityRules.d.ts +4 -0
- package/build/eslint/esm/src/pureFns/purityRules.js +67 -0
- package/build/eslint/esm/src/pureFns/purityRules.js.map +1 -0
- package/build/vite-plugin/cjs/index.cjs +14 -0
- package/build/vite-plugin/cjs/index.cjs.map +1 -0
- package/build/vite-plugin/cjs/package.json +1 -0
- package/build/vite-plugin/cjs/src/pureFns/purityRules.cjs +65 -0
- package/build/vite-plugin/cjs/src/pureFns/purityRules.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/pureFns/purityRules.d.ts +4 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotCacheGenerator.cjs +235 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotCacheGenerator.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotCacheGenerator.d.ts +23 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotDiskCache.cjs +134 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotDiskCache.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotDiskCache.d.ts +6 -0
- package/build/vite-plugin/cjs/src/vite-plugin/cjsPackageJsonPlugin.cjs +15 -0
- package/build/vite-plugin/cjs/src/vite-plugin/cjsPackageJsonPlugin.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/cjsPackageJsonPlugin.d.ts +2 -0
- package/build/vite-plugin/cjs/src/vite-plugin/constants.cjs +25 -0
- package/build/vite-plugin/cjs/src/vite-plugin/constants.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/constants.d.ts +10 -0
- package/build/vite-plugin/cjs/src/vite-plugin/extractPureFn.cjs +628 -0
- package/build/vite-plugin/cjs/src/vite-plugin/extractPureFn.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/extractPureFn.d.ts +13 -0
- package/build/vite-plugin/cjs/src/vite-plugin/index.cjs +14 -0
- package/build/vite-plugin/cjs/src/vite-plugin/index.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/index.d.ts +5 -0
- package/build/vite-plugin/cjs/src/vite-plugin/mionVitePlugin.cjs +404 -0
- package/build/vite-plugin/cjs/src/vite-plugin/mionVitePlugin.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/mionVitePlugin.d.ts +36 -0
- package/build/vite-plugin/cjs/src/vite-plugin/resolveModule.cjs +55 -0
- package/build/vite-plugin/cjs/src/vite-plugin/resolveModule.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/resolveModule.d.ts +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/transformers.cjs +165 -0
- package/build/vite-plugin/cjs/src/vite-plugin/transformers.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/transformers.d.ts +10 -0
- package/build/vite-plugin/cjs/src/vite-plugin/types.cjs +2 -0
- package/build/vite-plugin/cjs/src/vite-plugin/types.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/types.d.ts +57 -0
- package/build/vite-plugin/cjs/src/vite-plugin/virtual-modules.d.cjs +2 -0
- package/build/vite-plugin/cjs/src/vite-plugin/virtual-modules.d.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/virtualModule.cjs +68 -0
- package/build/vite-plugin/cjs/src/vite-plugin/virtualModule.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/virtualModule.d.ts +2 -0
- package/build/vite-plugin/esm/index.js +14 -0
- package/build/vite-plugin/esm/index.js.map +1 -0
- package/build/vite-plugin/esm/src/pureFns/purityRules.d.ts +4 -0
- package/build/vite-plugin/esm/src/pureFns/purityRules.js +65 -0
- package/build/vite-plugin/esm/src/pureFns/purityRules.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotCacheGenerator.d.ts +23 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotCacheGenerator.js +235 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotCacheGenerator.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotDiskCache.d.ts +6 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotDiskCache.js +133 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotDiskCache.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/cjsPackageJsonPlugin.d.ts +2 -0
- package/build/vite-plugin/esm/src/vite-plugin/cjsPackageJsonPlugin.js +15 -0
- package/build/vite-plugin/esm/src/vite-plugin/cjsPackageJsonPlugin.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/constants.d.ts +10 -0
- package/build/vite-plugin/esm/src/vite-plugin/constants.js +25 -0
- package/build/vite-plugin/esm/src/vite-plugin/constants.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/extractPureFn.d.ts +13 -0
- package/build/vite-plugin/esm/src/vite-plugin/extractPureFn.js +611 -0
- package/build/vite-plugin/esm/src/vite-plugin/extractPureFn.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/index.d.ts +5 -0
- package/build/vite-plugin/esm/src/vite-plugin/index.js +14 -0
- package/build/vite-plugin/esm/src/vite-plugin/index.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/mionVitePlugin.d.ts +36 -0
- package/build/vite-plugin/esm/src/vite-plugin/mionVitePlugin.js +387 -0
- package/build/vite-plugin/esm/src/vite-plugin/mionVitePlugin.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/resolveModule.d.ts +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/resolveModule.js +33 -0
- package/build/vite-plugin/esm/src/vite-plugin/resolveModule.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/transformers.d.ts +10 -0
- package/build/vite-plugin/esm/src/vite-plugin/transformers.js +148 -0
- package/build/vite-plugin/esm/src/vite-plugin/transformers.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/types.d.ts +57 -0
- package/build/vite-plugin/esm/src/vite-plugin/types.js +2 -0
- package/build/vite-plugin/esm/src/vite-plugin/types.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/virtual-modules.d.js +2 -0
- package/build/vite-plugin/esm/src/vite-plugin/virtual-modules.d.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/virtualModule.d.ts +2 -0
- package/build/vite-plugin/esm/src/vite-plugin/virtualModule.js +68 -0
- package/build/vite-plugin/esm/src/vite-plugin/virtualModule.js.map +1 -0
- package/package.json +94 -0
- package/src/vite-plugin/virtual-modules.d.ts +48 -0
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
import { createHash } from "crypto";
|
|
3
|
+
import { transformSync } from "esbuild";
|
|
4
|
+
import { BODY_HASH_LENGTH, PURE_SERVER_FN_NAMESPACE } from "./constants.js";
|
|
5
|
+
import { ALLOWED_GLOBALS, FORBIDDEN_IDENTIFIERS } from "../pureFns/purityRules.js";
|
|
6
|
+
import { readdirSync, statSync, readFileSync } from "fs";
|
|
7
|
+
import { resolve, join } from "path/posix";
|
|
8
|
+
import { isIncluded } from "./mionVitePlugin.js";
|
|
9
|
+
function extractVueScriptContent(source) {
|
|
10
|
+
const openRegex = /<script\b((?:[^>"']|"[^"]*"|'[^']*')*)>/gi;
|
|
11
|
+
const closeTag = "<\/script>";
|
|
12
|
+
let combined = "";
|
|
13
|
+
let lang = "js";
|
|
14
|
+
let found = false;
|
|
15
|
+
let openMatch;
|
|
16
|
+
while ((openMatch = openRegex.exec(source)) !== null) {
|
|
17
|
+
const attrs = openMatch[1];
|
|
18
|
+
const contentStart = openMatch.index + openMatch[0].length;
|
|
19
|
+
const closeIdx = findClosingScriptTag(source, contentStart, closeTag);
|
|
20
|
+
if (closeIdx === -1) break;
|
|
21
|
+
found = true;
|
|
22
|
+
combined += source.slice(contentStart, closeIdx) + "\n";
|
|
23
|
+
openRegex.lastIndex = closeIdx + closeTag.length;
|
|
24
|
+
const langMatch = attrs.match(/lang=["'](\w+)["']/);
|
|
25
|
+
if (langMatch) lang = langMatch[1];
|
|
26
|
+
}
|
|
27
|
+
return found ? { content: combined.trim(), lang } : null;
|
|
28
|
+
}
|
|
29
|
+
function findClosingScriptTag(source, start, closeTag) {
|
|
30
|
+
let i = start;
|
|
31
|
+
while (i < source.length) {
|
|
32
|
+
const ch = source[i];
|
|
33
|
+
if (ch === "'" || ch === '"' || ch === "`") {
|
|
34
|
+
i = skipStringLiteral(source, i, ch);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (ch === "<" && source.slice(i, i + closeTag.length).toLowerCase() === closeTag) {
|
|
38
|
+
return i;
|
|
39
|
+
}
|
|
40
|
+
i++;
|
|
41
|
+
}
|
|
42
|
+
return -1;
|
|
43
|
+
}
|
|
44
|
+
function skipStringLiteral(source, start, quote) {
|
|
45
|
+
let i = start + 1;
|
|
46
|
+
while (i < source.length) {
|
|
47
|
+
const ch = source[i];
|
|
48
|
+
if (ch === "\\") {
|
|
49
|
+
i += 2;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (ch === quote) return i + 1;
|
|
53
|
+
i++;
|
|
54
|
+
}
|
|
55
|
+
return i;
|
|
56
|
+
}
|
|
57
|
+
function scanClientSource(options) {
|
|
58
|
+
const include = options.include || ["**/*.ts", "**/*.tsx", "**/*.vue"];
|
|
59
|
+
const exclude = options.exclude || ["../node_modules/**", "**/.dist/**", "**/dist/**"];
|
|
60
|
+
const clientSrcPath = resolve(options.clientSrcPath);
|
|
61
|
+
const fns = [];
|
|
62
|
+
function scanDir(dir) {
|
|
63
|
+
const entries = readdirSync(dir);
|
|
64
|
+
for (const entry of entries) {
|
|
65
|
+
const fullPath = join(dir, entry);
|
|
66
|
+
const stat = statSync(fullPath);
|
|
67
|
+
if (stat.isDirectory()) {
|
|
68
|
+
if (!isIncluded(fullPath + "/", include, exclude)) continue;
|
|
69
|
+
scanDir(fullPath);
|
|
70
|
+
} else if (stat.isFile()) {
|
|
71
|
+
if (!isIncluded(fullPath, include, exclude)) continue;
|
|
72
|
+
try {
|
|
73
|
+
let code = readFileSync(fullPath, "utf-8");
|
|
74
|
+
let effectivePath = fullPath;
|
|
75
|
+
if (fullPath.endsWith(".vue")) {
|
|
76
|
+
const scriptBlock = extractVueScriptContent(code);
|
|
77
|
+
if (!scriptBlock) continue;
|
|
78
|
+
code = scriptBlock.content;
|
|
79
|
+
effectivePath = `${fullPath}.${scriptBlock.lang}`;
|
|
80
|
+
}
|
|
81
|
+
const hasPureFn = code.includes("pureServerFn");
|
|
82
|
+
const hasMapFrom = code.includes("mapFrom");
|
|
83
|
+
if (!hasPureFn && !hasMapFrom) continue;
|
|
84
|
+
if (hasPureFn) {
|
|
85
|
+
const extracted = extractPureFnsFromSource(code, effectivePath, "pureServerFn", options.noViteClient);
|
|
86
|
+
fns.push(...extracted);
|
|
87
|
+
}
|
|
88
|
+
if (hasMapFrom) {
|
|
89
|
+
const extracted = extractPureFnsFromSource(code, effectivePath, "mapFrom", options.noViteClient);
|
|
90
|
+
fns.push(...extracted);
|
|
91
|
+
}
|
|
92
|
+
} catch (err) {
|
|
93
|
+
console.warn(`[mion-pure-functions] Warning: Could not parse ${fullPath}: ${err.message}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
scanDir(clientSrcPath);
|
|
99
|
+
return fns;
|
|
100
|
+
}
|
|
101
|
+
function extractPureFnsFromSource(source, filePath, fnName = "pureServerFn", noViteClient = false) {
|
|
102
|
+
const results = [];
|
|
103
|
+
if (!source.includes(fnName)) return results;
|
|
104
|
+
const jsSource = stripTypes(source, filePath);
|
|
105
|
+
const sourceFile = ts.createSourceFile(filePath, jsSource, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
|
|
106
|
+
function visit(node) {
|
|
107
|
+
if (ts.isCallExpression(node)) {
|
|
108
|
+
const callee = node.expression;
|
|
109
|
+
if (ts.isIdentifier(callee) && callee.text === fnName) {
|
|
110
|
+
if (fnName === "registerPureFnFactory") {
|
|
111
|
+
const extracted = extractDataFromRegisterPureFnFactoryAST(node, sourceFile, filePath);
|
|
112
|
+
results.push(extracted);
|
|
113
|
+
} else if (fnName === "mapFrom") {
|
|
114
|
+
const extracted = extractDataFromMapFromCallAST(node, sourceFile, filePath, noViteClient);
|
|
115
|
+
results.push(extracted);
|
|
116
|
+
} else {
|
|
117
|
+
const extracted = extractDataFromPureFnDefAST(node, sourceFile, filePath, noViteClient);
|
|
118
|
+
results.push(extracted);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
ts.forEachChild(node, visit);
|
|
123
|
+
}
|
|
124
|
+
visit(sourceFile);
|
|
125
|
+
return results;
|
|
126
|
+
}
|
|
127
|
+
function stripTypes(code, filePath) {
|
|
128
|
+
try {
|
|
129
|
+
let loader = "ts";
|
|
130
|
+
if (filePath) {
|
|
131
|
+
if (filePath.endsWith(".tsx")) loader = "tsx";
|
|
132
|
+
else if (filePath.endsWith(".jsx")) loader = "jsx";
|
|
133
|
+
else if (filePath.endsWith(".js")) loader = "js";
|
|
134
|
+
}
|
|
135
|
+
const result = transformSync(code, {
|
|
136
|
+
loader,
|
|
137
|
+
target: "esnext",
|
|
138
|
+
minify: false
|
|
139
|
+
});
|
|
140
|
+
return result.code.trim();
|
|
141
|
+
} catch (err) {
|
|
142
|
+
throw new PurityError(err.message || String(err), "<esbuild>", 0);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function extractDataFromPureFnDefAST(call, sourceFile, filePath, noViteClient = false) {
|
|
146
|
+
if (call.arguments.length < 1 || call.arguments.length > 2) {
|
|
147
|
+
throw new PurityError(
|
|
148
|
+
"pureServerFn() requires 1 or 2 arguments: a function/PureFnDef and an optional name/bodyHash string",
|
|
149
|
+
filePath,
|
|
150
|
+
call.getStart(sourceFile)
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
let userProvidedName;
|
|
154
|
+
if (call.arguments.length === 2) {
|
|
155
|
+
const nameArg = call.arguments[1];
|
|
156
|
+
if (!ts.isStringLiteral(nameArg)) {
|
|
157
|
+
throw new PurityError(
|
|
158
|
+
"pureServerFn() second argument (name/bodyHash) must be a string literal",
|
|
159
|
+
filePath,
|
|
160
|
+
nameArg.getStart(sourceFile)
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
if (nameArg.text.length === 0) {
|
|
164
|
+
throw new PurityError(
|
|
165
|
+
"pureServerFn() second argument (name/bodyHash) must not be an empty string",
|
|
166
|
+
filePath,
|
|
167
|
+
nameArg.getStart(sourceFile)
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
userProvidedName = nameArg.text;
|
|
171
|
+
} else if (noViteClient) {
|
|
172
|
+
throw new PurityError(
|
|
173
|
+
"pureServerFn() requires a name as the second argument (string literal) when noViteClient is enabled",
|
|
174
|
+
filePath,
|
|
175
|
+
call.getStart(sourceFile)
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
let arg = call.arguments[0];
|
|
179
|
+
if (ts.isIdentifier(arg)) {
|
|
180
|
+
const resolved = resolveVariableInitializer(arg.text, sourceFile);
|
|
181
|
+
if (!resolved) {
|
|
182
|
+
if (isImportedIdentifier(arg.text, sourceFile)) {
|
|
183
|
+
throw new PurityError(
|
|
184
|
+
`pureServerFn() argument "${arg.text}" is imported from another module. Pure functions must be defined inline or as a variable in the same file`,
|
|
185
|
+
filePath,
|
|
186
|
+
arg.getStart(sourceFile)
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
throw new PurityError(
|
|
190
|
+
`pureServerFn() argument "${arg.text}" could not be resolved to a variable declaration in this file. Pure functions must be defined inline or as a variable in the same file`,
|
|
191
|
+
filePath,
|
|
192
|
+
arg.getStart(sourceFile)
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
arg = resolved;
|
|
196
|
+
}
|
|
197
|
+
if (ts.isFunctionExpression(arg) || ts.isArrowFunction(arg)) {
|
|
198
|
+
return buildExtractedPureFn(arg, PURE_SERVER_FN_NAMESPACE, void 0, false, sourceFile, filePath, userProvidedName);
|
|
199
|
+
}
|
|
200
|
+
if (ts.isObjectLiteralExpression(arg)) {
|
|
201
|
+
return extractPureFnDefFromObjectLiteral(arg, sourceFile, filePath, userProvidedName);
|
|
202
|
+
}
|
|
203
|
+
throw new PurityError(
|
|
204
|
+
"pureServerFn() first argument must be a function, an object literal (PureFnDef), or a variable referencing one",
|
|
205
|
+
filePath,
|
|
206
|
+
call.arguments[0].getStart(sourceFile)
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
function extractDataFromMapFromCallAST(call, sourceFile, filePath, noViteClient = false) {
|
|
210
|
+
if (call.arguments.length < 2 || call.arguments.length > 3) {
|
|
211
|
+
throw new PurityError(
|
|
212
|
+
"mapFrom() requires 2 or 3 arguments: a SubRequest source, a mapper function, and an optional name/bodyHash string",
|
|
213
|
+
filePath,
|
|
214
|
+
call.getStart(sourceFile)
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
let userProvidedName;
|
|
218
|
+
if (call.arguments.length === 3) {
|
|
219
|
+
const nameArg = call.arguments[2];
|
|
220
|
+
if (!ts.isStringLiteral(nameArg)) {
|
|
221
|
+
throw new PurityError(
|
|
222
|
+
"mapFrom() third argument (name/bodyHash) must be a string literal",
|
|
223
|
+
filePath,
|
|
224
|
+
nameArg.getStart(sourceFile)
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
if (nameArg.text.length === 0) {
|
|
228
|
+
throw new PurityError(
|
|
229
|
+
"mapFrom() third argument (name/bodyHash) must not be an empty string",
|
|
230
|
+
filePath,
|
|
231
|
+
nameArg.getStart(sourceFile)
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
userProvidedName = nameArg.text;
|
|
235
|
+
} else if (noViteClient) {
|
|
236
|
+
throw new PurityError(
|
|
237
|
+
"mapFrom() requires a name as the third argument (string literal) when noViteClient is enabled",
|
|
238
|
+
filePath,
|
|
239
|
+
call.getStart(sourceFile)
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
let arg = call.arguments[1];
|
|
243
|
+
if (ts.isIdentifier(arg)) {
|
|
244
|
+
const resolved = resolveVariableInitializer(arg.text, sourceFile);
|
|
245
|
+
if (!resolved) {
|
|
246
|
+
if (isImportedIdentifier(arg.text, sourceFile)) {
|
|
247
|
+
throw new PurityError(
|
|
248
|
+
`mapFrom() mapper argument "${arg.text}" is imported from another module. Pure functions must be defined inline or as a variable in the same file`,
|
|
249
|
+
filePath,
|
|
250
|
+
arg.getStart(sourceFile)
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
throw new PurityError(
|
|
254
|
+
`mapFrom() mapper argument "${arg.text}" could not be resolved to a variable declaration in this file. Pure functions must be defined inline or as a variable in the same file`,
|
|
255
|
+
filePath,
|
|
256
|
+
arg.getStart(sourceFile)
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
arg = resolved;
|
|
260
|
+
}
|
|
261
|
+
if (ts.isFunctionExpression(arg) || ts.isArrowFunction(arg)) {
|
|
262
|
+
return buildExtractedPureFn(arg, PURE_SERVER_FN_NAMESPACE, void 0, false, sourceFile, filePath, userProvidedName);
|
|
263
|
+
}
|
|
264
|
+
throw new PurityError(
|
|
265
|
+
"mapFrom() second argument (mapper) must be a function expression or arrow function",
|
|
266
|
+
filePath,
|
|
267
|
+
call.arguments[1].getStart(sourceFile)
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
function extractDataFromRegisterPureFnFactoryAST(call, sourceFile, filePath) {
|
|
271
|
+
if (call.arguments.length < 3 || call.arguments.length > 4) {
|
|
272
|
+
throw new PurityError(
|
|
273
|
+
"registerPureFnFactory() requires 3 or 4 arguments: namespace, functionID, factoryFn, and optional parsedFn",
|
|
274
|
+
filePath,
|
|
275
|
+
call.getStart(sourceFile)
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
const nsArg = call.arguments[0];
|
|
279
|
+
if (!ts.isStringLiteral(nsArg)) {
|
|
280
|
+
throw new PurityError(
|
|
281
|
+
"registerPureFnFactory() first argument (namespace) must be a string literal",
|
|
282
|
+
filePath,
|
|
283
|
+
nsArg.getStart(sourceFile)
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
const namespace = nsArg.text;
|
|
287
|
+
const idArg = call.arguments[1];
|
|
288
|
+
if (!ts.isStringLiteral(idArg)) {
|
|
289
|
+
throw new PurityError(
|
|
290
|
+
"registerPureFnFactory() second argument (functionID) must be a string literal",
|
|
291
|
+
filePath,
|
|
292
|
+
idArg.getStart(sourceFile)
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
const fnName = idArg.text;
|
|
296
|
+
const fnArg = call.arguments[2];
|
|
297
|
+
if (ts.isIdentifier(fnArg)) {
|
|
298
|
+
if (isImportedIdentifier(fnArg.text, sourceFile)) {
|
|
299
|
+
throw new PurityError(
|
|
300
|
+
`registerPureFnFactory() third argument "${fnArg.text}" is imported from another module. The factory function must be defined inline`,
|
|
301
|
+
filePath,
|
|
302
|
+
fnArg.getStart(sourceFile)
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
throw new PurityError(
|
|
306
|
+
`registerPureFnFactory() third argument "${fnArg.text}" could not be resolved. The factory function must be defined inline as a function expression or arrow function`,
|
|
307
|
+
filePath,
|
|
308
|
+
fnArg.getStart(sourceFile)
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
if (!ts.isFunctionExpression(fnArg) && !ts.isArrowFunction(fnArg)) {
|
|
312
|
+
throw new PurityError(
|
|
313
|
+
"registerPureFnFactory() third argument (factoryFn) must be a function expression or arrow function",
|
|
314
|
+
filePath,
|
|
315
|
+
fnArg.getStart(sourceFile)
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
const paramNames = fnArg.parameters.map((param) => {
|
|
319
|
+
if (!ts.isIdentifier(param.name)) {
|
|
320
|
+
throw new PurityError(
|
|
321
|
+
"Factory function parameters must be simple identifiers (no destructuring)",
|
|
322
|
+
filePath,
|
|
323
|
+
param.getStart(sourceFile)
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
return param.name.text;
|
|
327
|
+
});
|
|
328
|
+
const bodyNode = fnArg.body;
|
|
329
|
+
const bodyText = getBodyText(bodyNode, sourceFile);
|
|
330
|
+
const normalizedBody = bodyText.replace(/[ \t]+/g, " ").trim();
|
|
331
|
+
const bodyHash = createHash("sha256").update(namespace + fnName + normalizedBody).digest("base64url").slice(0, BODY_HASH_LENGTH);
|
|
332
|
+
return {
|
|
333
|
+
namespace,
|
|
334
|
+
fnName,
|
|
335
|
+
paramNames,
|
|
336
|
+
fnBody: bodyText,
|
|
337
|
+
bodyHash,
|
|
338
|
+
dependencies: /* @__PURE__ */ new Set(),
|
|
339
|
+
sourceFile: filePath,
|
|
340
|
+
isFactory: true
|
|
341
|
+
// registerPureFnFactory always registers factory functions
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
function isImportedIdentifier(name, sourceFile) {
|
|
345
|
+
for (const statement of sourceFile.statements) {
|
|
346
|
+
if (!ts.isImportDeclaration(statement) || !statement.importClause) continue;
|
|
347
|
+
const clause = statement.importClause;
|
|
348
|
+
if (clause.name && clause.name.text === name) return true;
|
|
349
|
+
if (clause.namedBindings) {
|
|
350
|
+
if (ts.isNamespaceImport(clause.namedBindings) && clause.namedBindings.name.text === name) return true;
|
|
351
|
+
if (ts.isNamedImports(clause.namedBindings)) {
|
|
352
|
+
for (const element of clause.namedBindings.elements) {
|
|
353
|
+
if (element.name.text === name) return true;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
function resolveVariableInitializer(name, sourceFile) {
|
|
361
|
+
let result;
|
|
362
|
+
function visit(node) {
|
|
363
|
+
if (result) return;
|
|
364
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.name.text === name && node.initializer) {
|
|
365
|
+
result = node.initializer;
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
ts.forEachChild(node, visit);
|
|
369
|
+
}
|
|
370
|
+
visit(sourceFile);
|
|
371
|
+
return result;
|
|
372
|
+
}
|
|
373
|
+
function extractPureFnDefFromObjectLiteral(objLiteral, sourceFile, filePath, userProvidedName) {
|
|
374
|
+
let pureFn;
|
|
375
|
+
let namespace = PURE_SERVER_FN_NAMESPACE;
|
|
376
|
+
let fnName;
|
|
377
|
+
let isFactory = false;
|
|
378
|
+
for (const prop of objLiteral.properties) {
|
|
379
|
+
if (!ts.isPropertyAssignment(prop)) continue;
|
|
380
|
+
const propName = ts.isIdentifier(prop.name) ? prop.name.text : void 0;
|
|
381
|
+
if (!propName) continue;
|
|
382
|
+
switch (propName) {
|
|
383
|
+
case "pureFn": {
|
|
384
|
+
const initializer = prop.initializer;
|
|
385
|
+
if (ts.isFunctionExpression(initializer) || ts.isArrowFunction(initializer)) {
|
|
386
|
+
pureFn = initializer;
|
|
387
|
+
} else if (ts.isIdentifier(initializer)) {
|
|
388
|
+
if (isImportedIdentifier(initializer.text, sourceFile)) {
|
|
389
|
+
throw new PurityError(
|
|
390
|
+
`pureFn property "${initializer.text}" is imported from another module. Pure functions must be defined inline or as a variable in the same file`,
|
|
391
|
+
filePath,
|
|
392
|
+
prop.initializer.getStart(sourceFile)
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
throw new PurityError(
|
|
396
|
+
`pureFn property "${initializer.text}" could not be resolved. Pure functions must be defined inline or as a variable in the same file`,
|
|
397
|
+
filePath,
|
|
398
|
+
prop.initializer.getStart(sourceFile)
|
|
399
|
+
);
|
|
400
|
+
} else {
|
|
401
|
+
throw new PurityError(
|
|
402
|
+
"pureFn property must be a function expression or arrow function",
|
|
403
|
+
filePath,
|
|
404
|
+
prop.initializer.getStart(sourceFile)
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
case "namespace":
|
|
410
|
+
if (ts.isStringLiteral(prop.initializer)) {
|
|
411
|
+
namespace = prop.initializer.text;
|
|
412
|
+
} else {
|
|
413
|
+
throw new PurityError(
|
|
414
|
+
"namespace property must be a string literal",
|
|
415
|
+
filePath,
|
|
416
|
+
prop.initializer.getStart(sourceFile)
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
break;
|
|
420
|
+
case "fnName":
|
|
421
|
+
if (ts.isStringLiteral(prop.initializer)) {
|
|
422
|
+
fnName = prop.initializer.text;
|
|
423
|
+
} else {
|
|
424
|
+
throw new PurityError(
|
|
425
|
+
"fnName property must be a string literal",
|
|
426
|
+
filePath,
|
|
427
|
+
prop.initializer.getStart(sourceFile)
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
break;
|
|
431
|
+
case "isFactory":
|
|
432
|
+
if (prop.initializer.kind === ts.SyntaxKind.TrueKeyword) {
|
|
433
|
+
isFactory = true;
|
|
434
|
+
} else if (prop.initializer.kind === ts.SyntaxKind.FalseKeyword) {
|
|
435
|
+
isFactory = false;
|
|
436
|
+
} else {
|
|
437
|
+
throw new PurityError(
|
|
438
|
+
"isFactory property must be a boolean literal",
|
|
439
|
+
filePath,
|
|
440
|
+
prop.initializer.getStart(sourceFile)
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
break;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (!pureFn) {
|
|
447
|
+
throw new PurityError("PureFnDef must have a pureFn property", filePath, objLiteral.getStart(sourceFile));
|
|
448
|
+
}
|
|
449
|
+
const explicitFnName = fnName ?? (ts.isFunctionExpression(pureFn) && pureFn.name ? pureFn.name.text : void 0);
|
|
450
|
+
return buildExtractedPureFn(pureFn, namespace, explicitFnName, isFactory, sourceFile, filePath, userProvidedName);
|
|
451
|
+
}
|
|
452
|
+
function buildExtractedPureFn(fnNode, namespace, explicitFnName, isFactory, sourceFile, filePath, userProvidedName) {
|
|
453
|
+
const paramNames = fnNode.parameters.map((param) => {
|
|
454
|
+
if (!ts.isIdentifier(param.name)) {
|
|
455
|
+
throw new PurityError(
|
|
456
|
+
"Pure function parameters must be simple identifiers (no destructuring)",
|
|
457
|
+
filePath,
|
|
458
|
+
param.getStart(sourceFile)
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
return param.name.text;
|
|
462
|
+
});
|
|
463
|
+
const bodyNode = fnNode.body;
|
|
464
|
+
const fnTypeLabel = isFactory ? "factory functions" : "pure functions";
|
|
465
|
+
validatePurity(bodyNode, new Set(paramNames), explicitFnName, sourceFile, filePath, fnTypeLabel);
|
|
466
|
+
const bodyText = getBodyText(bodyNode, sourceFile);
|
|
467
|
+
if (userProvidedName) {
|
|
468
|
+
return {
|
|
469
|
+
namespace,
|
|
470
|
+
fnName: userProvidedName,
|
|
471
|
+
paramNames,
|
|
472
|
+
fnBody: bodyText,
|
|
473
|
+
bodyHash: userProvidedName,
|
|
474
|
+
dependencies: /* @__PURE__ */ new Set(),
|
|
475
|
+
sourceFile: filePath,
|
|
476
|
+
isFactory
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
const normalizedBody = bodyText.replace(/[ \t]+/g, " ").trim();
|
|
480
|
+
const bodyHash = createHash("sha256").update(namespace + normalizedBody).digest("base64url").slice(0, BODY_HASH_LENGTH);
|
|
481
|
+
const fnName = explicitFnName || bodyHash;
|
|
482
|
+
return {
|
|
483
|
+
namespace,
|
|
484
|
+
fnName,
|
|
485
|
+
paramNames,
|
|
486
|
+
fnBody: bodyText,
|
|
487
|
+
bodyHash,
|
|
488
|
+
dependencies: /* @__PURE__ */ new Set(),
|
|
489
|
+
sourceFile: filePath,
|
|
490
|
+
isFactory
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
function getBodyText(body, sourceFile) {
|
|
494
|
+
if (ts.isBlock(body)) {
|
|
495
|
+
const fullText = body.getText(sourceFile);
|
|
496
|
+
return fullText.slice(1, -1).trim();
|
|
497
|
+
} else {
|
|
498
|
+
return `return ${body.getText(sourceFile)}`;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
function validatePurity(body, localScope, fnName, sourceFile, filePath, fnTypeLabel = "pure functions") {
|
|
502
|
+
collectLocalDeclarations(body, localScope);
|
|
503
|
+
if (fnName) localScope.add(fnName);
|
|
504
|
+
function checkNode(node) {
|
|
505
|
+
if (node.kind === ts.SyntaxKind.ThisKeyword) {
|
|
506
|
+
throw new PurityError(`'this' is not allowed in ${fnTypeLabel}`, filePath, node.getStart(sourceFile));
|
|
507
|
+
}
|
|
508
|
+
if (ts.isAwaitExpression(node)) {
|
|
509
|
+
throw new PurityError(`async/await is not allowed in ${fnTypeLabel}`, filePath, node.getStart(sourceFile));
|
|
510
|
+
}
|
|
511
|
+
if (node.kind === ts.SyntaxKind.YieldKeyword) {
|
|
512
|
+
throw new PurityError(`generators are not allowed in ${fnTypeLabel}`, filePath, node.getStart(sourceFile));
|
|
513
|
+
}
|
|
514
|
+
if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
|
515
|
+
throw new PurityError(`Dynamic import() is not allowed in ${fnTypeLabel}`, filePath, node.getStart(sourceFile));
|
|
516
|
+
}
|
|
517
|
+
if (ts.isIdentifier(node)) {
|
|
518
|
+
const name = node.text;
|
|
519
|
+
if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
|
|
520
|
+
ts.forEachChild(node, checkNode);
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
if (ts.isPropertyAssignment(node.parent) && node.parent.name === node) {
|
|
524
|
+
ts.forEachChild(node, checkNode);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
if (ts.isShorthandPropertyAssignment(node.parent) && node.parent.name === node) {
|
|
528
|
+
if (!localScope.has(name) && !ALLOWED_GLOBALS.has(name)) {
|
|
529
|
+
throw new PurityError(
|
|
530
|
+
`Closure variable "${name}" is not allowed in ${fnTypeLabel}. ${fnTypeLabel[0].toUpperCase() + fnTypeLabel.slice(1)} cannot access outer scope variables.`,
|
|
531
|
+
filePath,
|
|
532
|
+
node.getStart(sourceFile)
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
ts.forEachChild(node, checkNode);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
if (FORBIDDEN_IDENTIFIERS.has(name)) {
|
|
539
|
+
throw new PurityError(`${name} is not allowed in ${fnTypeLabel}`, filePath, node.getStart(sourceFile));
|
|
540
|
+
}
|
|
541
|
+
if (!localScope.has(name) && !ALLOWED_GLOBALS.has(name)) {
|
|
542
|
+
throw new PurityError(
|
|
543
|
+
`Closure variable "${name}" is not allowed in ${fnTypeLabel}. ${fnTypeLabel[0].toUpperCase() + fnTypeLabel.slice(1)} cannot access outer scope variables.`,
|
|
544
|
+
filePath,
|
|
545
|
+
node.getStart(sourceFile)
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
ts.forEachChild(node, checkNode);
|
|
550
|
+
}
|
|
551
|
+
checkNode(body);
|
|
552
|
+
}
|
|
553
|
+
function collectLocalDeclarations(node, scope) {
|
|
554
|
+
function visit(n) {
|
|
555
|
+
if (ts.isVariableDeclaration(n)) {
|
|
556
|
+
collectBindingNames(n.name, scope);
|
|
557
|
+
}
|
|
558
|
+
if (ts.isFunctionDeclaration(n) && n.name) {
|
|
559
|
+
scope.add(n.name.text);
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
if (ts.isFunctionExpression(n) && n.name) {
|
|
563
|
+
scope.add(n.name.text);
|
|
564
|
+
n.parameters.forEach((p) => collectBindingNames(p.name, scope));
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
if (ts.isArrowFunction(n)) {
|
|
568
|
+
n.parameters.forEach((p) => collectBindingNames(p.name, scope));
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
if (ts.isForOfStatement(n) || ts.isForInStatement(n)) {
|
|
572
|
+
if (ts.isVariableDeclarationList(n.initializer)) {
|
|
573
|
+
n.initializer.declarations.forEach((d) => collectBindingNames(d.name, scope));
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
if (ts.isCatchClause(n) && n.variableDeclaration) {
|
|
577
|
+
collectBindingNames(n.variableDeclaration.name, scope);
|
|
578
|
+
}
|
|
579
|
+
ts.forEachChild(n, visit);
|
|
580
|
+
}
|
|
581
|
+
visit(node);
|
|
582
|
+
}
|
|
583
|
+
function collectBindingNames(name, scope) {
|
|
584
|
+
if (ts.isIdentifier(name)) {
|
|
585
|
+
scope.add(name.text);
|
|
586
|
+
} else if (ts.isObjectBindingPattern(name)) {
|
|
587
|
+
name.elements.forEach((el) => collectBindingNames(el.name, scope));
|
|
588
|
+
} else if (ts.isArrayBindingPattern(name)) {
|
|
589
|
+
name.elements.forEach((el) => {
|
|
590
|
+
if (ts.isBindingElement(el)) {
|
|
591
|
+
collectBindingNames(el.name, scope);
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
class PurityError extends Error {
|
|
597
|
+
constructor(message, filePath, position) {
|
|
598
|
+
super(`${message} (in ${filePath} at position ${position})`);
|
|
599
|
+
this.filePath = filePath;
|
|
600
|
+
this.position = position;
|
|
601
|
+
this.name = "PurityError";
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
export {
|
|
605
|
+
PurityError,
|
|
606
|
+
extractPureFnsFromSource,
|
|
607
|
+
extractVueScriptContent,
|
|
608
|
+
scanClientSource,
|
|
609
|
+
stripTypes
|
|
610
|
+
};
|
|
611
|
+
//# sourceMappingURL=extractPureFn.js.map
|