@valbuild/server 0.146.4 → 0.148.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/CHANGELOG.md +129 -0
- package/browser/dist/valbuild-server-browser.cjs.d.ts +2 -0
- package/browser/dist/valbuild-server-browser.cjs.dev.js +146 -0
- package/browser/dist/valbuild-server-browser.cjs.js +7 -0
- package/browser/dist/valbuild-server-browser.cjs.prod.js +146 -0
- package/browser/dist/valbuild-server-browser.esm.js +142 -0
- package/browser/package.json +4 -0
- package/dist/ValRouterCore-42772c21.cjs.prod.js +10824 -0
- package/dist/ValRouterCore-7ca3dc68.esm.js +10784 -0
- package/dist/ValRouterCore-9bdaaf89.cjs.dev.js +10824 -0
- package/dist/declarations/src/ValOps.d.ts +44 -11
- package/dist/declarations/src/ValOpsFS.d.ts +1 -0
- package/dist/declarations/src/ValOpsHttp.d.ts +1 -0
- package/dist/declarations/src/ValOpsMemory.d.ts +13 -4
- package/dist/declarations/src/ValRouter.d.ts +1 -1
- package/dist/declarations/src/ValServer.d.ts +9 -3
- package/dist/declarations/src/browser/inTabFetch.d.ts +27 -0
- package/dist/declarations/src/browser/index.d.ts +9 -0
- package/dist/declarations/src/checkRemoteRef.d.ts +40 -2
- package/dist/declarations/src/createFixPatch.d.ts +25 -3
- package/dist/declarations/src/fixFiles.d.ts +42 -0
- package/dist/declarations/src/fixHandlers.d.ts +15 -1
- package/dist/declarations/src/getSettings.d.ts +1 -0
- package/dist/declarations/src/index.d.ts +1 -0
- package/dist/declarations/src/linkCheck/probeUrl.d.ts +40 -0
- package/dist/declarations/src/platform.d.ts +55 -0
- package/dist/declarations/src/remoteFileCheck.d.ts +116 -0
- package/dist/declarations/src/remoteHostCheck.d.ts +101 -0
- package/dist/declarations/src/studioValidation.d.ts +84 -0
- package/dist/declarations/src/uploadRemoteFile.d.ts +1 -1
- package/dist/declarations/src/videosetFixes.d.ts +3 -2
- package/dist/getJsonEntryAtCommit-2e36e805.cjs.prod.js +142 -0
- package/dist/getJsonEntryAtCommit-40b6d9c1.esm.js +135 -0
- package/dist/getJsonEntryAtCommit-64f67a50.cjs.dev.js +142 -0
- package/dist/jsonValuesModule-427d68c8.esm.js +273 -0
- package/dist/jsonValuesModule-8ae503a8.cjs.dev.js +288 -0
- package/dist/jsonValuesModule-d1bb8d8f.cjs.prod.js +288 -0
- package/dist/ops-0d05feaf.esm.js +353 -0
- package/dist/ops-b15239ac.cjs.prod.js +361 -0
- package/dist/ops-f37d18c6.cjs.dev.js +361 -0
- package/dist/publishKit-580f558a.cjs.prod.js +41 -0
- package/dist/publishKit-5865095c.cjs.dev.js +41 -0
- package/dist/publishKit-bc8df53d.esm.js +31 -0
- package/dist/valbuild-server.cjs.dev.js +9239 -18895
- package/dist/valbuild-server.cjs.prod.js +9239 -18895
- package/dist/valbuild-server.esm.js +9107 -18763
- package/package.json +17 -5
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var ts = require('typescript');
|
|
4
|
+
var ValRouterCore = require('./ValRouterCore-9bdaaf89.cjs.dev.js');
|
|
5
|
+
var fp = require('@valbuild/core/fp');
|
|
6
|
+
var jsonValuesModule = require('./jsonValuesModule-8ae503a8.cjs.dev.js');
|
|
7
|
+
require('@valbuild/core');
|
|
8
|
+
require('@valbuild/shared/internal');
|
|
9
|
+
require('zod');
|
|
10
|
+
require('zod-validation-error');
|
|
11
|
+
require('@valbuild/core/patch');
|
|
12
|
+
require('image-size');
|
|
13
|
+
|
|
14
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
|
15
|
+
|
|
16
|
+
var ts__default = /*#__PURE__*/_interopDefault(ts);
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* One `.jsonValues()` entry's content, as it was at a commit.
|
|
20
|
+
*
|
|
21
|
+
* The history pane renders a commit through the real field components, and a
|
|
22
|
+
* `.jsonValues()` record's source is only `{_type:"json"}` markers - the
|
|
23
|
+
* content lives in a separate `*.val.json` per entry, fetched on demand. The
|
|
24
|
+
* live Studio fetches it from `GET /json`; the commit pane had nothing to
|
|
25
|
+
* fetch from, so every entry stayed a marker and rendered as though the field
|
|
26
|
+
* were empty. Which is worse than an error: an empty value is a claim, and it
|
|
27
|
+
* was a false one.
|
|
28
|
+
*
|
|
29
|
+
* ## Why this needs the module's own `.val.ts`
|
|
30
|
+
*
|
|
31
|
+
* There is no derivable relationship between a record key and its entry file.
|
|
32
|
+
* From the example app:
|
|
33
|
+
*
|
|
34
|
+
* "/support/getting-started": c.json(() => import("./content/getting-started.val.json"))
|
|
35
|
+
* "/support/faq": c.json(() => import("./content/faq.val.json"))
|
|
36
|
+
*
|
|
37
|
+
* The key is a route and the path is whatever the author wrote. The only place
|
|
38
|
+
* the two are related is the `import()` literal inside the `.val.ts`, which is
|
|
39
|
+
* why `findJsonEntryFilePathsInSource` reads them out of the AST too. The
|
|
40
|
+
* commit archive stores the module's SOURCE (the markers) and its schema, not
|
|
41
|
+
* its text - so the text is fetched from git at that commit and parsed here.
|
|
42
|
+
*
|
|
43
|
+
* Two round trips per entry, then: the module, and the entry. The module's
|
|
44
|
+
* parse is worth caching per (commit, module) by the caller; nothing here does,
|
|
45
|
+
* because a `ValOps` is not a cache and the endpoint above it is the layer that
|
|
46
|
+
* knows how long a commit stays interesting.
|
|
47
|
+
*/
|
|
48
|
+
async function getJsonEntryAtCommit(ops, commitSha, moduleFilePath, key) {
|
|
49
|
+
const moduleGitPath = ops.gitPathOfModule(moduleFilePath);
|
|
50
|
+
if (fp.result.isErr(moduleGitPath)) {
|
|
51
|
+
return moduleGitPath;
|
|
52
|
+
}
|
|
53
|
+
const moduleRes = await ops.getFileAtCommit(commitSha, moduleGitPath.value, false);
|
|
54
|
+
if (fp.result.isErr(moduleRes)) {
|
|
55
|
+
return moduleRes;
|
|
56
|
+
}
|
|
57
|
+
const entryPath = findEntryImportPath(moduleFilePath, moduleGitPath.value, ValRouterCore.bytesToUtf8(moduleRes.value), key);
|
|
58
|
+
if (fp.result.isErr(entryPath)) {
|
|
59
|
+
return entryPath;
|
|
60
|
+
}
|
|
61
|
+
// The entry path resolved above is PROJECT-relative, like the module path it
|
|
62
|
+
// came from; the git path needs the root in front of it, and the ops are what
|
|
63
|
+
// know the root. Reusing gitPathOfModule rather than re-deriving the prefix:
|
|
64
|
+
// a `.val.json` is not a module, but "project-relative path -> repo path" is
|
|
65
|
+
// the same question and one implementation of it is the point.
|
|
66
|
+
const entryGitPath = ops.gitPathOfModule(entryPath.value);
|
|
67
|
+
if (fp.result.isErr(entryGitPath)) {
|
|
68
|
+
return entryGitPath;
|
|
69
|
+
}
|
|
70
|
+
const entryRes = await ops.getFileAtCommit(commitSha, entryGitPath.value, false);
|
|
71
|
+
if (fp.result.isErr(entryRes)) {
|
|
72
|
+
return entryRes;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
return fp.result.ok(JSON.parse(ValRouterCore.bytesToUtf8(entryRes.value)));
|
|
76
|
+
} catch (err) {
|
|
77
|
+
return fp.result.err({
|
|
78
|
+
kind: "file-unavailable",
|
|
79
|
+
gitPath: entryGitPath.value,
|
|
80
|
+
message: `not valid JSON at this commit: ${err instanceof Error ? err.message : String(err)}`
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The `import()` path a key's entry is behind, from the module's text.
|
|
87
|
+
*
|
|
88
|
+
* Exported for its own test: this is the part with a real chance of being
|
|
89
|
+
* wrong, and it is pure.
|
|
90
|
+
*/
|
|
91
|
+
function findEntryImportPath(moduleFilePath,
|
|
92
|
+
/**
|
|
93
|
+
* The same module, repository-relative, for error reporting only.
|
|
94
|
+
*
|
|
95
|
+
* A `gitPath` in a HistoryError is a REPOSITORY path - that is what every
|
|
96
|
+
* other history helper reports, and what a reader can paste into a `git show`
|
|
97
|
+
* - so reporting the project-relative ModuleFilePath here named a file that
|
|
98
|
+
* does not exist at that path for any project not rooted at the repo root.
|
|
99
|
+
*/
|
|
100
|
+
moduleGitPath, valTsSource, key) {
|
|
101
|
+
const sourceFile = ts__default["default"].createSourceFile(moduleFilePath, valTsSource, ts__default["default"].ScriptTarget.ES2015, true);
|
|
102
|
+
let analysis;
|
|
103
|
+
try {
|
|
104
|
+
analysis = jsonValuesModule.analyzeValModule(sourceFile);
|
|
105
|
+
} catch (err) {
|
|
106
|
+
// `file-unavailable` rather than a kind of its own: the module's TEXT is
|
|
107
|
+
// the file we could not use, and adding a wire kind for "unparseable"
|
|
108
|
+
// would be a new case every reader has to handle to say the same thing.
|
|
109
|
+
return fp.result.err({
|
|
110
|
+
kind: "file-unavailable",
|
|
111
|
+
gitPath: moduleGitPath,
|
|
112
|
+
message: `could not parse the module at this commit: ${err instanceof Error ? err.message : String(err)}`
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (fp.result.isErr(analysis)) {
|
|
116
|
+
return fp.result.err({
|
|
117
|
+
kind: "file-unavailable",
|
|
118
|
+
gitPath: moduleGitPath,
|
|
119
|
+
message: "could not read the module at this commit"
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
const entries = jsonValuesModule.analyzeJsonValuesEntries(analysis.value.source);
|
|
123
|
+
const entry = entries.get(key);
|
|
124
|
+
if (entry === undefined) {
|
|
125
|
+
/*
|
|
126
|
+
* Reported, not treated as empty. A key with no entry at this commit means
|
|
127
|
+
* the entry was added later - so "there is nothing to show for it here" is
|
|
128
|
+
* the true answer, and rendering an empty field instead would say the
|
|
129
|
+
* author had left it blank.
|
|
130
|
+
*/
|
|
131
|
+
return fp.result.err({
|
|
132
|
+
kind: "file-unavailable",
|
|
133
|
+
gitPath: moduleGitPath,
|
|
134
|
+
message: `'${key}' had no entry at this commit`
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
const moduleDir = ValRouterCore.posixPath.dirname(moduleFilePath);
|
|
138
|
+
return fp.result.ok(ValRouterCore.posixPath.join(moduleDir, entry.importPath));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
exports.findEntryImportPath = findEntryImportPath;
|
|
142
|
+
exports.getJsonEntryAtCommit = getJsonEntryAtCommit;
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import { pipe, result } from '@valbuild/core/fp';
|
|
3
|
+
|
|
4
|
+
class ValSyntaxError {
|
|
5
|
+
constructor(message, node) {
|
|
6
|
+
this.message = message;
|
|
7
|
+
this.node = node;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function forEachError(tree, callback) {
|
|
11
|
+
if (Array.isArray(tree)) {
|
|
12
|
+
for (const subtree of tree) {
|
|
13
|
+
forEachError(subtree, callback);
|
|
14
|
+
}
|
|
15
|
+
} else {
|
|
16
|
+
callback(tree);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function flatMapErrors(tree, cb) {
|
|
20
|
+
const result = [];
|
|
21
|
+
forEachError(tree, error => result.push(cb(error)));
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
function formatSyntaxError(error, sourceFile) {
|
|
25
|
+
const pos = sourceFile.getLineAndCharacterOfPosition(error.node.pos);
|
|
26
|
+
return `${pos.line}:${pos.character} ${error.message}`;
|
|
27
|
+
}
|
|
28
|
+
function formatSyntaxErrorTree(tree, sourceFile) {
|
|
29
|
+
return flatMapErrors(tree, error => formatSyntaxError(error, sourceFile));
|
|
30
|
+
}
|
|
31
|
+
function isLiteralPropertyName(name) {
|
|
32
|
+
return ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name);
|
|
33
|
+
}
|
|
34
|
+
function validateObjectProperties(nodes) {
|
|
35
|
+
const errors = [];
|
|
36
|
+
for (const node of nodes) {
|
|
37
|
+
if (!ts.isPropertyAssignment(node)) {
|
|
38
|
+
errors.push(new ValSyntaxError("Object literal element must be property assignment", node));
|
|
39
|
+
} else if (!isLiteralPropertyName(node.name)) {
|
|
40
|
+
errors.push(new ValSyntaxError("Object literal element key must be an identifier or a literal", node));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (errors.length > 0) {
|
|
44
|
+
return result.err(errors);
|
|
45
|
+
}
|
|
46
|
+
return result.ok(nodes);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Validates that the expression is a JSON compatible type of expression without
|
|
51
|
+
* validating its children.
|
|
52
|
+
*/
|
|
53
|
+
function shallowValidateExpression(value) {
|
|
54
|
+
return ts.isStringLiteralLike(value) || ts.isNumericLiteral(value) || value.kind === ts.SyntaxKind.TrueKeyword || value.kind === ts.SyntaxKind.FalseKeyword || value.kind === ts.SyntaxKind.NullKeyword || ts.isArrayLiteralExpression(value) || ts.isObjectLiteralExpression(value) ? undefined : new ValSyntaxError("Expression must be a literal", value);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Evaluates the expression as a JSON value
|
|
59
|
+
*/
|
|
60
|
+
function evaluateExpression(value) {
|
|
61
|
+
// The text property of a LiteralExpression stores the interpreted value of the literal in text form. For a StringLiteral,
|
|
62
|
+
// or any literal of a template, this means quotes have been removed and escapes have been converted to actual characters.
|
|
63
|
+
// For a NumericLiteral, the stored value is the toString() representation of the number. For example 1, 1.00, and 1e0 are all stored as just "1".
|
|
64
|
+
// https://github.com/microsoft/TypeScript/blob/4b794fe1dd0d184d3f8f17e94d8187eace57c91e/src/compiler/types.ts#L2127-L2131
|
|
65
|
+
|
|
66
|
+
if (ts.isStringLiteralLike(value)) {
|
|
67
|
+
return result.ok(value.text);
|
|
68
|
+
} else if (ts.isNumericLiteral(value)) {
|
|
69
|
+
return result.ok(Number(value.text));
|
|
70
|
+
} else if (value.kind === ts.SyntaxKind.TrueKeyword) {
|
|
71
|
+
return result.ok(true);
|
|
72
|
+
} else if (value.kind === ts.SyntaxKind.FalseKeyword) {
|
|
73
|
+
return result.ok(false);
|
|
74
|
+
} else if (value.kind === ts.SyntaxKind.NullKeyword) {
|
|
75
|
+
return result.ok(null);
|
|
76
|
+
} else if (ts.isArrayLiteralExpression(value)) {
|
|
77
|
+
return result.all(value.elements.map(evaluateExpression));
|
|
78
|
+
} else if (ts.isObjectLiteralExpression(value)) {
|
|
79
|
+
return pipe(validateObjectProperties(value.properties), result.flatMap(assignments => pipe(assignments.map(assignment => pipe(evaluateExpression(assignment.initializer), result.map(value => [assignment.name.text, value]))), result.all)), result.map(Object.fromEntries));
|
|
80
|
+
} else {
|
|
81
|
+
return result.err(new ValSyntaxError("Expression must be a literal", value));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function findObjectPropertyAssignment(value, key) {
|
|
85
|
+
return pipe(validateObjectProperties(value.properties), result.flatMap(assignments => {
|
|
86
|
+
const matchingAssignments = assignments.filter(assignment => assignment.name.text === key);
|
|
87
|
+
if (matchingAssignments.length === 0) return result.ok(undefined);
|
|
88
|
+
if (matchingAssignments.length > 1) {
|
|
89
|
+
return result.err(new ValSyntaxError(`Object key "${key}" is ambiguous`, value));
|
|
90
|
+
}
|
|
91
|
+
const [assignment] = matchingAssignments;
|
|
92
|
+
return result.ok(assignment);
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
function validateInitializers(nodes) {
|
|
96
|
+
for (const node of nodes) {
|
|
97
|
+
if (ts.isSpreadElement(node)) {
|
|
98
|
+
return new ValSyntaxError("Unexpected spread element", node);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function isPath(node, path) {
|
|
105
|
+
let currentNode = node;
|
|
106
|
+
for (let i = path.length - 1; i > 0; --i) {
|
|
107
|
+
const name = path[i];
|
|
108
|
+
if (!ts.isPropertyAccessExpression(currentNode)) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
if (!ts.isIdentifier(currentNode.name) || currentNode.name.text !== name) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
currentNode = currentNode.expression;
|
|
115
|
+
}
|
|
116
|
+
return ts.isIdentifier(currentNode) && currentNode.text === path[0];
|
|
117
|
+
}
|
|
118
|
+
function validateArguments(node, validators) {
|
|
119
|
+
return result.allV([node.arguments.length === validators.length ? result.voidOk : result.err(new ValSyntaxError(`Expected ${validators.length} arguments`, node)), ...node.arguments.slice(0, validators.length).map((argument, index) => validators[index](argument))]);
|
|
120
|
+
}
|
|
121
|
+
function analyzeDefaultExport(node) {
|
|
122
|
+
const cDefine = node.expression;
|
|
123
|
+
if (!ts.isCallExpression(cDefine)) {
|
|
124
|
+
return result.err(new ValSyntaxError("Expected default expression to be a call expression", cDefine));
|
|
125
|
+
}
|
|
126
|
+
if (!isPath(cDefine.expression, ["c", "define"])) {
|
|
127
|
+
return result.err(new ValSyntaxError("Expected default expression to be calling c.define", cDefine.expression));
|
|
128
|
+
}
|
|
129
|
+
return pipe(validateArguments(cDefine, [id => {
|
|
130
|
+
// TODO: validate ID value here?
|
|
131
|
+
if (!ts.isStringLiteralLike(id)) {
|
|
132
|
+
return result.err(new ValSyntaxError("Expected first argument to c.define to be a string literal", id));
|
|
133
|
+
}
|
|
134
|
+
return result.voidOk;
|
|
135
|
+
}, () => {
|
|
136
|
+
return result.voidOk;
|
|
137
|
+
}, () => {
|
|
138
|
+
return result.voidOk;
|
|
139
|
+
}]), result.map(() => {
|
|
140
|
+
const [, schema, source] = cDefine.arguments;
|
|
141
|
+
return {
|
|
142
|
+
schema,
|
|
143
|
+
source
|
|
144
|
+
};
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
function analyzeValModule(sourceFile) {
|
|
148
|
+
const analysis = sourceFile.forEachChild(node => {
|
|
149
|
+
if (ts.isExportAssignment(node)) {
|
|
150
|
+
return analyzeDefaultExport(node);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
if (!analysis) {
|
|
154
|
+
throw Error("Failed to find fixed content node in val module");
|
|
155
|
+
}
|
|
156
|
+
return analysis;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Given the `source` object-literal expression of a `.jsonValues()` record /
|
|
161
|
+
* router (the 3rd argument to `c.define`, via {@link analyzeValModule}),
|
|
162
|
+
* extracts each entry's `c.json(() => import("..."))` import path, keyed by
|
|
163
|
+
* entry key. Entries whose value is not a `c.json(...)` call are skipped.
|
|
164
|
+
*/
|
|
165
|
+
function analyzeJsonValuesEntries(source) {
|
|
166
|
+
const entries = new Map();
|
|
167
|
+
if (!ts.isObjectLiteralExpression(source)) {
|
|
168
|
+
return entries;
|
|
169
|
+
}
|
|
170
|
+
for (const prop of source.properties) {
|
|
171
|
+
if (!ts.isPropertyAssignment(prop)) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
const key = getPropertyKey(prop.name);
|
|
175
|
+
if (key === null) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
const entry = analyzeCJsonCall(prop.initializer);
|
|
179
|
+
if (entry) {
|
|
180
|
+
entries.set(key, entry);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return entries;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Every key of a `.jsonValues()` record's source literal, thunked or not.
|
|
188
|
+
*
|
|
189
|
+
* {@link analyzeJsonValuesEntries} answers "which entries have a file behind
|
|
190
|
+
* them", which is the question for resolving a path. This is the other one:
|
|
191
|
+
* which keys the record HAS. They differ on a value that is not a
|
|
192
|
+
* `c.json(...)` call - written inline, or mid-edit - and telling an existing
|
|
193
|
+
* key from a new one is what decides whether a whole-record write adds a
|
|
194
|
+
* `c.json(...)` thunk or replaces one. Using the thunk set for that would
|
|
195
|
+
* insert a second property with a key the literal already has.
|
|
196
|
+
*/
|
|
197
|
+
function jsonValuesRecordKeys(source) {
|
|
198
|
+
const keys = [];
|
|
199
|
+
if (!ts.isObjectLiteralExpression(source)) {
|
|
200
|
+
return keys;
|
|
201
|
+
}
|
|
202
|
+
for (const prop of source.properties) {
|
|
203
|
+
if (!ts.isPropertyAssignment(prop)) {
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
const key = getPropertyKey(prop.name);
|
|
207
|
+
if (key !== null) {
|
|
208
|
+
keys.push(key);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return keys;
|
|
212
|
+
}
|
|
213
|
+
function getPropertyKey(name) {
|
|
214
|
+
if (ts.isStringLiteralLike(name)) {
|
|
215
|
+
return name.text;
|
|
216
|
+
}
|
|
217
|
+
if (ts.isIdentifier(name)) {
|
|
218
|
+
return name.text;
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
function analyzeCJsonCall(node) {
|
|
223
|
+
// c.json(() => import("path"))
|
|
224
|
+
if (!ts.isCallExpression(node) || !isCJson(node.expression)) {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
const [thunk] = node.arguments;
|
|
228
|
+
if (!thunk) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
const importPath = getImportPathFromThunk(thunk);
|
|
232
|
+
if (importPath === null) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
importPath
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function isCJson(expr) {
|
|
240
|
+
// c.json
|
|
241
|
+
return ts.isPropertyAccessExpression(expr) && ts.isIdentifier(expr.expression) && expr.expression.text === "c" && ts.isIdentifier(expr.name) && expr.name.text === "json";
|
|
242
|
+
}
|
|
243
|
+
function getImportPathFromThunk(thunk) {
|
|
244
|
+
// () => import("path") (concise body) OR () => { return import("path"); }
|
|
245
|
+
if (!ts.isArrowFunction(thunk)) {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
let importCall;
|
|
249
|
+
if (ts.isCallExpression(thunk.body)) {
|
|
250
|
+
importCall = thunk.body;
|
|
251
|
+
} else if (ts.isBlock(thunk.body)) {
|
|
252
|
+
for (const stmt of thunk.body.statements) {
|
|
253
|
+
if (ts.isReturnStatement(stmt) && stmt.expression && ts.isCallExpression(stmt.expression)) {
|
|
254
|
+
importCall = stmt.expression;
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (!importCall || !ts.isCallExpression(importCall)) {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
const isImport = importCall.expression.kind === ts.SyntaxKind.ImportKeyword || ts.isIdentifier(importCall.expression) && importCall.expression.text === "import";
|
|
263
|
+
if (!isImport) {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
const arg = importCall.arguments[0];
|
|
267
|
+
if (arg && ts.isStringLiteralLike(arg)) {
|
|
268
|
+
return arg.text;
|
|
269
|
+
}
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export { analyzeValModule as a, formatSyntaxError as b, analyzeJsonValuesEntries as c, formatSyntaxErrorTree as d, findObjectPropertyAssignment as e, flatMapErrors as f, evaluateExpression as g, jsonValuesRecordKeys as j, shallowValidateExpression as s, validateInitializers as v };
|