zitejs 0.10.0-start.7 → 0.10.0-start.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/auth/index.d.ts +2 -2
- package/dist/cjs/check/index.d.ts +33 -1
- package/dist/cjs/check/index.js +136 -86
- package/dist/cjs/check/start/describeRules.d.ts +35 -0
- package/dist/cjs/check/start/describeRules.js +89 -0
- package/dist/cjs/check/start/problem.d.ts +11 -0
- package/dist/cjs/check/start/problem.js +5 -0
- package/dist/cjs/check/start/sourceRules.d.ts +3 -0
- package/dist/cjs/check/start/sourceRules.js +441 -0
- package/dist/cjs/cli.js +1 -1
- package/dist/cjs/sync/lib.js +4 -7
- package/dist/cjs/vite/index.js +8 -3
- package/dist/cjs/vite/index.test.js +10 -2
- package/dist/esm/auth/index.d.ts +2 -2
- package/dist/esm/check/index.d.ts +33 -1
- package/dist/esm/check/index.js +135 -88
- package/dist/esm/check/start/describeRules.d.ts +35 -0
- package/dist/esm/check/start/describeRules.js +86 -0
- package/dist/esm/check/start/problem.d.ts +11 -0
- package/dist/esm/check/start/problem.js +1 -0
- package/dist/esm/check/start/sourceRules.d.ts +3 -0
- package/dist/esm/check/start/sourceRules.js +438 -0
- package/dist/esm/cli.js +1 -1
- package/dist/esm/start/airtable/arrayFields.d.ts +10 -0
- package/dist/esm/start/airtable/arrayFields.js +30 -0
- package/dist/esm/start/airtable/dispatch.d.ts +54 -0
- package/dist/esm/start/airtable/dispatch.js +465 -0
- package/dist/esm/start/airtable/formulas.d.ts +69 -0
- package/dist/esm/start/airtable/formulas.js +235 -0
- package/dist/esm/start/airtable/index.d.ts +11 -0
- package/dist/esm/start/airtable/index.js +70 -0
- package/dist/esm/start/data/baseRunner.d.ts +92 -0
- package/dist/esm/start/data/baseRunner.js +83 -0
- package/dist/esm/start/data/dispatch.d.ts +16 -0
- package/dist/esm/start/data/dispatch.js +198 -0
- package/dist/esm/start/data/index.d.ts +18 -0
- package/dist/esm/start/data/index.js +76 -0
- package/dist/esm/start/data/sqlNameMappings.d.ts +58 -0
- package/dist/esm/start/data/sqlNameMappings.js +92 -0
- package/dist/esm/start/data/transformations.d.ts +128 -0
- package/dist/esm/start/data/transformations.js +318 -0
- package/dist/esm/start/data/types.d.ts +91 -0
- package/dist/esm/start/data/types.js +14 -0
- package/dist/esm/start/describe.d.ts +5 -0
- package/dist/esm/start/describe.js +8 -1
- package/dist/esm/start/errors.d.ts +5 -0
- package/dist/esm/start/errors.js +5 -0
- package/dist/esm/start/factories.test-d.d.ts +1 -0
- package/dist/esm/start/factories.test-d.js +20 -0
- package/dist/esm/start/index.d.ts +7 -4
- package/dist/esm/start/index.js +2 -5
- package/dist/esm/start/router.d.ts +1 -0
- package/dist/esm/start/router.js +7 -3
- package/dist/esm/start/server.js +54 -5
- package/dist/esm/start/types.d.ts +40 -0
- package/dist/esm/start/version.d.ts +1 -1
- package/dist/esm/start/version.js +1 -1
- package/dist/esm/start/vite.js +2 -1
- package/dist/esm/start/workflow.d.ts +3 -0
- package/dist/esm/start/workflow.test-d.js +5 -0
- package/dist/esm/sync/lib.js +4 -7
- package/dist/esm/vite/index.js +8 -3
- package/dist/esm/vite/index.test.js +10 -2
- package/package.json +1 -1
package/dist/esm/check/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { execFileSync } from 'child_process';
|
|
2
|
-
import { existsSync } from 'fs';
|
|
3
|
-
import { join } from 'path';
|
|
2
|
+
import { existsSync, readdirSync, readFileSync } from 'fs';
|
|
3
|
+
import { join, relative } from 'path';
|
|
4
4
|
import { listEndpointNames, listSourceDirs } from '../sourceRoots.js';
|
|
5
|
+
import { formatProblem } from './start/problem.js';
|
|
5
6
|
/**
|
|
6
7
|
* An argument array, not a command string, so no shell is involved.
|
|
7
8
|
*
|
|
@@ -57,41 +58,132 @@ function bundleFailures(output) {
|
|
|
57
58
|
}
|
|
58
59
|
const indent = (output) => output.trim().split('\n').map(l => ` ${l}`).join('\n');
|
|
59
60
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* refuses, read the same way.
|
|
61
|
+
* While `--json` checks, stdout carries only the report: app code that logs as
|
|
62
|
+
* it's described in-process goes to stderr instead.
|
|
63
63
|
*/
|
|
64
|
-
async
|
|
65
|
-
process.stdout.write
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
export const withStdoutOnStderr = async (run) => {
|
|
65
|
+
const write = process.stdout.write;
|
|
66
|
+
process.stdout.write = process.stderr.write.bind(process.stderr);
|
|
67
|
+
try {
|
|
68
|
+
return await run();
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
process.stdout.write = write;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
/** Prints as it goes, or with `--json` stays quiet and reports everything at the end. */
|
|
75
|
+
export class Reporter {
|
|
76
|
+
json;
|
|
77
|
+
apps = [];
|
|
78
|
+
constructor(json) {
|
|
79
|
+
this.json = json;
|
|
80
|
+
}
|
|
81
|
+
app(path, runtime, heading) {
|
|
82
|
+
const app = { path, runtime, ok: true, steps: [], problems: [] };
|
|
83
|
+
this.apps.push(app);
|
|
84
|
+
if (heading && !this.json)
|
|
85
|
+
console.log(`\n── ${path} ──`);
|
|
86
|
+
return app;
|
|
87
|
+
}
|
|
88
|
+
step(app, name, result) {
|
|
89
|
+
const output = result.output?.trim();
|
|
90
|
+
app.steps.push({ name, ok: result.ok, ...(output && !result.ok && { output }) });
|
|
91
|
+
if (!result.ok)
|
|
92
|
+
app.ok = false;
|
|
93
|
+
if (!this.json) {
|
|
94
|
+
console.log(` ${name} ... ${result.ok ? '✓' : '✗'}`);
|
|
95
|
+
if (!result.ok && output)
|
|
96
|
+
console.log(indent(output));
|
|
97
|
+
}
|
|
98
|
+
return result.ok;
|
|
99
|
+
}
|
|
100
|
+
problems(app, problems) {
|
|
101
|
+
app.problems.push(...problems);
|
|
102
|
+
if (problems.some(p => p.severity === 'error'))
|
|
103
|
+
app.ok = false;
|
|
104
|
+
if (!this.json && problems.length)
|
|
105
|
+
console.log(problems.map(p => ` ${formatProblem(p)}`).join('\n'));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const SKIPPED_DIRS = new Set(['node_modules', 'dist', '.output', '.tanstack', '.wrangler']);
|
|
109
|
+
/** The app's own source, by app-relative path. */
|
|
110
|
+
function readAppSources(appPath) {
|
|
111
|
+
const sources = {};
|
|
112
|
+
const visit = (dir) => {
|
|
113
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
114
|
+
if (SKIPPED_DIRS.has(entry.name))
|
|
115
|
+
continue;
|
|
116
|
+
const full = join(dir, entry.name);
|
|
117
|
+
if (entry.isDirectory())
|
|
118
|
+
visit(full);
|
|
119
|
+
else if (/\.[cm]?[jt]sx?$/.test(entry.name))
|
|
120
|
+
sources[relative(appPath, full)] = readFileSync(full, 'utf-8');
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
const src = join(appPath, 'src');
|
|
124
|
+
if (existsSync(src))
|
|
125
|
+
visit(src);
|
|
126
|
+
return sources;
|
|
127
|
+
}
|
|
128
|
+
const isInternalApp = (appPath) => {
|
|
129
|
+
try {
|
|
130
|
+
return JSON.parse(readFileSync(join(appPath, 'zite.config.json'), 'utf-8'))?.accessMode === 'internal';
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
70
133
|
return false;
|
|
71
134
|
}
|
|
72
|
-
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* A Start app: `vite build` first, since it regenerates the route tree tsc
|
|
138
|
+
* reads, then tsc, then describe, which reports what the platform's build
|
|
139
|
+
* refuses, read the same way, then the source hints.
|
|
140
|
+
*/
|
|
141
|
+
async function checkStartApp(reporter, app, appPath) {
|
|
142
|
+
if (!reporter.step(app, 'vite build', run('npx', ['vite', 'build'], appPath)))
|
|
143
|
+
return;
|
|
73
144
|
// --pretty for `file:line:col - error` lines, the shape the platform re-roots.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
console.log(indent(tsc.output));
|
|
78
|
-
process.stdout.write(' describe ... ');
|
|
145
|
+
reporter.step(app, 'tsc --noEmit', run('npx', ['tsc', '--noEmit', '--pretty', '-p', 'tsconfig.app.json'], appPath));
|
|
146
|
+
const { describedProblems } = await import('./start/describeRules.js');
|
|
147
|
+
const { sourceProblems } = await import('./start/sourceRules.js');
|
|
79
148
|
try {
|
|
80
149
|
const { describeBuiltApp } = await import('../start/describeBuilt.js');
|
|
81
|
-
const
|
|
82
|
-
const problems =
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
console.log(problems.map(p => ` ${p}`).join('\n'));
|
|
86
|
-
return tsc.ok && problems.length === 0;
|
|
150
|
+
const description = (await describeBuiltApp(appPath));
|
|
151
|
+
const problems = describedProblems(description, { internal: isInternalApp(appPath) });
|
|
152
|
+
reporter.step(app, 'describe', { ok: !problems.some(p => p.severity === 'error') });
|
|
153
|
+
reporter.problems(app, problems);
|
|
87
154
|
}
|
|
88
155
|
catch (err) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
156
|
+
reporter.step(app, 'describe', { ok: false, output: err instanceof Error ? err.message : String(err) });
|
|
157
|
+
}
|
|
158
|
+
const hints = sourceProblems(readAppSources(appPath));
|
|
159
|
+
reporter.step(app, 'source', { ok: !hints.some(p => p.severity === 'error') });
|
|
160
|
+
reporter.problems(app, hints);
|
|
161
|
+
}
|
|
162
|
+
function checkClassicApp(reporter, app, root, dir, appPath) {
|
|
163
|
+
// Only tsconfig.app.json compiles anything. The app's tsconfig.json is a
|
|
164
|
+
// solution-style config — `"files": []` plus a reference — so
|
|
165
|
+
// `tsc --noEmit -p tsconfig.json` type-checks zero files and exits 0. It
|
|
166
|
+
// used to be the fallback, which meant an app missing tsconfig.app.json
|
|
167
|
+
// reported a passing typecheck it never ran.
|
|
168
|
+
const tsconfigAppPath = join(appPath, 'tsconfig.app.json');
|
|
169
|
+
reporter.step(app, 'tsc --noEmit', existsSync(tsconfigAppPath)
|
|
170
|
+
? run('npx', ['tsc', '--noEmit', '-p', tsconfigAppPath], '.')
|
|
171
|
+
: { ok: false, output: `no ${tsconfigAppPath}` });
|
|
172
|
+
// Endpoints never reach vite — they are bundled separately for the lambda,
|
|
173
|
+
// against a different resolver. tsc and vite both pass on an endpoint whose
|
|
174
|
+
// `zitejs/*` alias has no generated file behind it; only the bundler knows.
|
|
175
|
+
if (listEndpointNames(root, appPath).length > 0) {
|
|
176
|
+
const bundle = run('npx', ['zitejs', 'bundle', '--app', dir], '.');
|
|
177
|
+
const failures = bundle.ok ? bundleFailures(bundle.output) : [bundle.output];
|
|
178
|
+
reporter.step(app, 'bundle endpoints', { ok: failures.length === 0, output: failures.join('\n') });
|
|
179
|
+
}
|
|
180
|
+
if (existsSync(join(appPath, 'vite.config.ts'))) {
|
|
181
|
+
reporter.step(app, 'vite build', run('npx', ['vite', 'build'], appPath));
|
|
92
182
|
}
|
|
93
183
|
}
|
|
94
|
-
export async function runCheck() {
|
|
184
|
+
export async function runCheck(args = process.argv.slice(3)) {
|
|
185
|
+
const json = args.includes('--json');
|
|
186
|
+
const reporter = new Reporter(json);
|
|
95
187
|
const { isStartAppDir } = await import('../start/entries.js');
|
|
96
188
|
// Run inside a Start app, its own zitejs checks just that app: the workspace
|
|
97
189
|
// root's is a classic release that doesn't know Start.
|
|
@@ -101,72 +193,27 @@ export async function runCheck() {
|
|
|
101
193
|
console.error('No apps or automations found under apps/ or automations/.');
|
|
102
194
|
process.exit(1);
|
|
103
195
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (
|
|
109
|
-
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
// Only tsconfig.app.json compiles anything. The app's tsconfig.json is a
|
|
113
|
-
// solution-style config — `"files": []` plus a reference — so
|
|
114
|
-
// `tsc --noEmit -p tsconfig.json` type-checks zero files and exits 0. It
|
|
115
|
-
// used to be the fallback, which meant an app missing tsconfig.app.json
|
|
116
|
-
// reported a passing typecheck it never ran.
|
|
117
|
-
const tsconfigAppPath = join(appPath, 'tsconfig.app.json');
|
|
118
|
-
if (!existsSync(tsconfigAppPath)) {
|
|
119
|
-
console.log(` tsc --noEmit ... ✗ (no ${tsconfigAppPath})`);
|
|
120
|
-
allPassed = false;
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
process.stdout.write(' tsc --noEmit ... ');
|
|
124
|
-
const tsc = run('npx', ['tsc', '--noEmit', '-p', tsconfigAppPath], '.');
|
|
125
|
-
if (tsc.ok) {
|
|
126
|
-
console.log('✓');
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
console.log('✗');
|
|
130
|
-
console.log(tsc.output.trim().split('\n').map(l => ` ${l}`).join('\n'));
|
|
131
|
-
allPassed = false;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
// Endpoints never reach vite — they are bundled separately for the lambda,
|
|
135
|
-
// against a different resolver. tsc and vite both pass on an endpoint whose
|
|
136
|
-
// `zitejs/*` alias has no generated file behind it; only the bundler knows.
|
|
137
|
-
if (listEndpointNames(root, appPath).length > 0) {
|
|
138
|
-
process.stdout.write(' bundle endpoints ... ');
|
|
139
|
-
const bundle = run('npx', ['zitejs', 'bundle', '--app', app], '.');
|
|
140
|
-
const failures = bundle.ok ? bundleFailures(bundle.output) : [bundle.output];
|
|
141
|
-
if (failures.length === 0) {
|
|
142
|
-
console.log('✓');
|
|
196
|
+
const checkAll = async () => {
|
|
197
|
+
if (inStartApp)
|
|
198
|
+
await checkStartApp(reporter, reporter.app('.', 'start', false), '.');
|
|
199
|
+
for (const { root, dir, path: appPath } of appDirs) {
|
|
200
|
+
if (isStartAppDir(appPath)) {
|
|
201
|
+
await checkStartApp(reporter, reporter.app(appPath, 'start', true), appPath);
|
|
143
202
|
}
|
|
144
203
|
else {
|
|
145
|
-
|
|
146
|
-
console.log(failures.map(l => ` ${l}`).join('\n'));
|
|
147
|
-
allPassed = false;
|
|
204
|
+
checkClassicApp(reporter, reporter.app(appPath, 'classic', true), root, dir, appPath);
|
|
148
205
|
}
|
|
149
206
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
console.log('✓');
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
console.log('✗');
|
|
159
|
-
console.log(vite.output.trim().split('\n').map(l => ` ${l}`).join('\n'));
|
|
160
|
-
allPassed = false;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
console.log('');
|
|
165
|
-
if (allPassed) {
|
|
166
|
-
console.log('All apps pass.');
|
|
207
|
+
};
|
|
208
|
+
await (json ? withStdoutOnStderr(checkAll) : checkAll());
|
|
209
|
+
const allPassed = reporter.apps.every(app => app.ok);
|
|
210
|
+
if (json) {
|
|
211
|
+
process.stdout.write(`${JSON.stringify({ ok: allPassed, apps: reporter.apps })}\n`);
|
|
167
212
|
}
|
|
168
213
|
else {
|
|
169
|
-
console.log('
|
|
170
|
-
|
|
214
|
+
console.log('');
|
|
215
|
+
console.log(allPassed ? 'All apps pass.' : 'Some apps failed.');
|
|
171
216
|
}
|
|
217
|
+
if (!allPassed)
|
|
218
|
+
process.exit(1);
|
|
172
219
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Problem } from "./problem.js";
|
|
2
|
+
type Access = {
|
|
3
|
+
kind: string;
|
|
4
|
+
roles?: string[];
|
|
5
|
+
};
|
|
6
|
+
export type CheckedDescription = {
|
|
7
|
+
functions: Array<{
|
|
8
|
+
id: string;
|
|
9
|
+
name?: string;
|
|
10
|
+
file?: string;
|
|
11
|
+
access: Access;
|
|
12
|
+
input?: unknown;
|
|
13
|
+
problems: string[];
|
|
14
|
+
}>;
|
|
15
|
+
routes: Array<{
|
|
16
|
+
path: string;
|
|
17
|
+
file?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
methods: Record<string, Access>;
|
|
20
|
+
problems: string[];
|
|
21
|
+
}>;
|
|
22
|
+
pages?: Array<{
|
|
23
|
+
path: string;
|
|
24
|
+
file?: string;
|
|
25
|
+
}>;
|
|
26
|
+
workflows?: Array<{
|
|
27
|
+
id: string;
|
|
28
|
+
file: string;
|
|
29
|
+
problems: string[];
|
|
30
|
+
}>;
|
|
31
|
+
};
|
|
32
|
+
export declare function describedProblems(description: CheckedDescription, { internal }: {
|
|
33
|
+
internal: boolean;
|
|
34
|
+
}): Problem[];
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// The host, TanStack and Cloudflare answer these before the app does.
|
|
2
|
+
const RESERVED_PATHS = [
|
|
3
|
+
/^\/_zite/,
|
|
4
|
+
/^\/__zite/,
|
|
5
|
+
/^\/_serverFn(\/|$)/,
|
|
6
|
+
/^\/cdn-cgi(\/|$)/,
|
|
7
|
+
/^\/auth(\/|$)/,
|
|
8
|
+
/^\/api\/auth(\/|$)/,
|
|
9
|
+
/^\/manifest\.webmanifest$/,
|
|
10
|
+
];
|
|
11
|
+
const OWNER_INPUTS = ["userId", "ownerId"];
|
|
12
|
+
/** `access-missing: no signedIn…`, or a route's `GET: access-missing: …`, as a named problem. */
|
|
13
|
+
const describeProblem = (problem, subject, file) => {
|
|
14
|
+
const match = /^(?:(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS|ANY): )?([a-z][a-z-]*): (.*)$/s.exec(problem);
|
|
15
|
+
const [, method, rule, message] = match ?? [undefined, undefined, "describe", problem];
|
|
16
|
+
return {
|
|
17
|
+
rule: rule ?? "describe",
|
|
18
|
+
severity: "error",
|
|
19
|
+
...(file && { file }),
|
|
20
|
+
message: `${subject}${method ? ` ${method}` : ""}: ${message ?? problem}`,
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
const inputProperties = (input) => {
|
|
24
|
+
const properties = input?.properties;
|
|
25
|
+
return properties && typeof properties === "object" ? Object.keys(properties) : [];
|
|
26
|
+
};
|
|
27
|
+
export function describedProblems(description, { internal }) {
|
|
28
|
+
const problems = [];
|
|
29
|
+
for (const fn of description.functions) {
|
|
30
|
+
const subject = `function ${fn.name ?? fn.id}`;
|
|
31
|
+
problems.push(...fn.problems.map((p) => describeProblem(p, subject, fn.file)));
|
|
32
|
+
if (internal && fn.access.kind === "anyone") {
|
|
33
|
+
problems.push({
|
|
34
|
+
rule: "public-in-internal",
|
|
35
|
+
severity: "error",
|
|
36
|
+
...(fn.file && { file: fn.file }),
|
|
37
|
+
message: `${subject} is a publicFn, and this internal app signs everyone in; make it a signedInFn`,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
const owner = fn.access.kind === "signedIn" && inputProperties(fn.input).find((p) => OWNER_INPUTS.includes(p));
|
|
41
|
+
if (owner) {
|
|
42
|
+
problems.push({
|
|
43
|
+
rule: "owner-from-input",
|
|
44
|
+
severity: "warning",
|
|
45
|
+
...(fn.file && { file: fn.file }),
|
|
46
|
+
message: `${subject} takes ${owner} as input, which any caller can set; use context.user.id`,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
for (const route of description.routes) {
|
|
51
|
+
const subject = `route ${route.path}`;
|
|
52
|
+
problems.push(...route.problems.map((p) => describeProblem(p, subject, route.file)));
|
|
53
|
+
const publicMethods = Object.entries(route.methods).filter(([, access]) => access.kind === "anyone");
|
|
54
|
+
if (internal && publicMethods.length > 0) {
|
|
55
|
+
problems.push({
|
|
56
|
+
rule: "public-in-internal",
|
|
57
|
+
severity: "error",
|
|
58
|
+
...(route.file && { file: route.file }),
|
|
59
|
+
message: `${subject} lets anyone call ${publicMethods.map(([m]) => m).join(", ")}, and this internal app signs everyone in; use signedIn`,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (!route.description) {
|
|
63
|
+
problems.push({
|
|
64
|
+
rule: "description-missing",
|
|
65
|
+
severity: "warning",
|
|
66
|
+
...(route.file && { file: route.file }),
|
|
67
|
+
message: `${subject} has no description; add staticData: { description } for the editor`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
for (const workflow of description.workflows ?? []) {
|
|
72
|
+
problems.push(...workflow.problems.map((p) => describeProblem(p, `workflow ${workflow.id}`, workflow.file)));
|
|
73
|
+
}
|
|
74
|
+
const paths = new Map([...description.routes, ...(description.pages ?? [])].map((r) => [r.path, r.file]));
|
|
75
|
+
for (const [path, file] of paths) {
|
|
76
|
+
if (RESERVED_PATHS.some((reserved) => reserved.test(path))) {
|
|
77
|
+
problems.push({
|
|
78
|
+
rule: "reserved-path",
|
|
79
|
+
severity: "error",
|
|
80
|
+
...(file && { file }),
|
|
81
|
+
message: `${path} is the platform's; give the route another path`,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return problems;
|
|
86
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type Severity = "error" | "warning";
|
|
2
|
+
/** One finding of `zitejs check`, named by its rule. Errors fail the check; warnings only report. */
|
|
3
|
+
export type Problem = {
|
|
4
|
+
rule: string;
|
|
5
|
+
severity: Severity;
|
|
6
|
+
/** App-relative. */
|
|
7
|
+
file?: string;
|
|
8
|
+
line?: number;
|
|
9
|
+
message: string;
|
|
10
|
+
};
|
|
11
|
+
export declare const formatProblem: ({ rule, severity, file, line, message }: Problem) => string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const formatProblem = ({ rule, severity, file, line, message }) => `${severity} ${rule}${file ? ` ${file}${line ? `:${line}` : ""}` : ""}: ${message}`;
|