readyup 0.0.0 → 0.13.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 +123 -0
- package/bin/rdy.js +11 -0
- package/dist/esm/.cache +1 -0
- package/dist/esm/assertIsPreflightCollection.d.ts +2 -0
- package/dist/esm/assertIsPreflightCollection.js +27 -0
- package/dist/esm/assertIsRdyKit.d.ts +2 -0
- package/dist/esm/assertIsRdyKit.js +27 -0
- package/dist/esm/authoring.d.ts +6 -0
- package/dist/esm/authoring.js +22 -0
- package/dist/esm/bin/preflight.d.ts +1 -0
- package/dist/esm/bin/preflight.js +12 -0
- package/dist/esm/bin/rdy.d.ts +1 -0
- package/dist/esm/bin/rdy.js +12 -0
- package/dist/esm/bin/route.d.ts +1 -0
- package/dist/esm/bin/route.js +202 -0
- package/dist/esm/check-utils/filesystem.d.ts +4 -0
- package/dist/esm/check-utils/filesystem.js +26 -0
- package/dist/esm/check-utils/index.d.ts +3 -0
- package/dist/esm/check-utils/index.js +14 -0
- package/dist/esm/check-utils/package-json.d.ts +6 -0
- package/dist/esm/check-utils/package-json.js +40 -0
- package/dist/esm/check-utils/semver.d.ts +1 -0
- package/dist/esm/check-utils/semver.js +12 -0
- package/dist/esm/cli.d.ts +36 -0
- package/dist/esm/cli.js +285 -0
- package/dist/esm/compile/compileCommand.d.ts +1 -0
- package/dist/esm/compile/compileCommand.js +121 -0
- package/dist/esm/compile/compileConfig.d.ts +5 -0
- package/dist/esm/compile/compileConfig.js +52 -0
- package/dist/esm/compile/validateCompiledOutput.d.ts +1 -0
- package/dist/esm/compile/validateCompiledOutput.js +29 -0
- package/dist/esm/config.d.ts +2 -0
- package/dist/esm/config.js +28 -0
- package/dist/esm/expandGitHubShorthand.d.ts +1 -0
- package/dist/esm/expandGitHubShorthand.js +26 -0
- package/dist/esm/formatCombinedSummary.d.ts +2 -0
- package/dist/esm/formatCombinedSummary.js +40 -0
- package/dist/esm/formatJsonError.d.ts +1 -0
- package/dist/esm/formatJsonError.js +6 -0
- package/dist/esm/formatJsonReport.d.ts +10 -0
- package/dist/esm/formatJsonReport.js +90 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +37 -0
- package/dist/esm/init/initCommand.d.ts +6 -0
- package/dist/esm/init/initCommand.js +33 -0
- package/dist/esm/init/scaffold.d.ts +11 -0
- package/dist/esm/init/scaffold.js +15 -0
- package/dist/esm/init/templates.d.ts +2 -0
- package/dist/esm/init/templates.js +37 -0
- package/dist/esm/isRecord.d.ts +1 -0
- package/dist/esm/isRecord.js +6 -0
- package/dist/esm/jitiImport.d.ts +1 -0
- package/dist/esm/jitiImport.js +23 -0
- package/dist/esm/loadConfig.d.ts +2 -0
- package/dist/esm/loadConfig.js +74 -0
- package/dist/esm/loadRemoteCollection.d.ts +6 -0
- package/dist/esm/loadRemoteCollection.js +40 -0
- package/dist/esm/loadRemoteKit.d.ts +6 -0
- package/dist/esm/loadRemoteKit.js +40 -0
- package/dist/esm/parseArgs.d.ts +15 -0
- package/dist/esm/parseArgs.js +93 -0
- package/dist/esm/reportPreflight.d.ts +7 -0
- package/dist/esm/reportPreflight.js +105 -0
- package/dist/esm/reportRdy.d.ts +7 -0
- package/dist/esm/reportRdy.js +105 -0
- package/dist/esm/resolveCollectionExports.d.ts +1 -0
- package/dist/esm/resolveCollectionExports.js +20 -0
- package/dist/esm/resolveGitHubToken.d.ts +1 -0
- package/dist/esm/resolveGitHubToken.js +22 -0
- package/dist/esm/resolveKitExports.d.ts +1 -0
- package/dist/esm/resolveKitExports.js +20 -0
- package/dist/esm/resolveRequestedNames.d.ts +2 -0
- package/dist/esm/resolveRequestedNames.js +38 -0
- package/dist/esm/runPreflight.d.ts +7 -0
- package/dist/esm/runPreflight.js +157 -0
- package/dist/esm/runRdy.d.ts +7 -0
- package/dist/esm/runRdy.js +157 -0
- package/dist/esm/terminal.d.ts +6 -0
- package/dist/esm/terminal.js +55 -0
- package/dist/esm/types.d.ts +139 -0
- package/dist/esm/types.js +10 -0
- package/dist/esm/validateCollection.d.ts +2 -0
- package/dist/esm/validateCollection.js +27 -0
- package/dist/esm/validateKit.d.ts +2 -0
- package/dist/esm/validateKit.js +27 -0
- package/dist/esm/version.d.ts +1 -0
- package/dist/esm/version.js +4 -0
- package/dist/esm/writeFileWithCheck.d.ts +10 -0
- package/dist/esm/writeFileWithCheck.js +41 -0
- package/package.json +56 -10
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { meetsThreshold } from "./runRdy.js";
|
|
2
|
+
function formatJsonReport(entries, options) {
|
|
3
|
+
const reportOn = options?.reportOn ?? "recommend";
|
|
4
|
+
let totalPassed = 0;
|
|
5
|
+
let totalFailed = 0;
|
|
6
|
+
let totalSkipped = 0;
|
|
7
|
+
const checklists = entries.map(({ name, report }) => {
|
|
8
|
+
let passed = 0;
|
|
9
|
+
let failed = 0;
|
|
10
|
+
let skipped = 0;
|
|
11
|
+
const visibleResults = report.results.filter((r) => meetsThreshold(r.severity, reportOn));
|
|
12
|
+
for (const result of visibleResults) {
|
|
13
|
+
if (result.status === "passed") passed++;
|
|
14
|
+
else if (result.status === "failed") failed++;
|
|
15
|
+
else skipped++;
|
|
16
|
+
}
|
|
17
|
+
const { entries: checks } = buildCheckEntries(visibleResults, 0, 0);
|
|
18
|
+
totalPassed += passed;
|
|
19
|
+
totalFailed += failed;
|
|
20
|
+
totalSkipped += skipped;
|
|
21
|
+
return {
|
|
22
|
+
name,
|
|
23
|
+
allPassed: report.passed,
|
|
24
|
+
durationMs: report.durationMs,
|
|
25
|
+
passed,
|
|
26
|
+
failed,
|
|
27
|
+
skipped,
|
|
28
|
+
checks
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
const totalDurationMs = checklists.reduce((sum, c) => sum + c.durationMs, 0);
|
|
32
|
+
const output = {
|
|
33
|
+
allPassed: entries.every(({ report }) => report.passed),
|
|
34
|
+
passed: totalPassed,
|
|
35
|
+
failed: totalFailed,
|
|
36
|
+
skipped: totalSkipped,
|
|
37
|
+
durationMs: totalDurationMs,
|
|
38
|
+
checklists
|
|
39
|
+
};
|
|
40
|
+
return JSON.stringify(output);
|
|
41
|
+
}
|
|
42
|
+
function buildCheckEntries(results, startIndex, expectedDepth) {
|
|
43
|
+
const entries = [];
|
|
44
|
+
let index = startIndex;
|
|
45
|
+
while (index < results.length) {
|
|
46
|
+
const result = results[index];
|
|
47
|
+
if (result === void 0) break;
|
|
48
|
+
const depth = result.depth;
|
|
49
|
+
if (depth < expectedDepth) break;
|
|
50
|
+
index++;
|
|
51
|
+
const { entries: children, nextIndex } = buildCheckEntries(results, index, depth + 1);
|
|
52
|
+
index = nextIndex;
|
|
53
|
+
entries.push(buildCheckEntry(result, children));
|
|
54
|
+
}
|
|
55
|
+
return { entries, nextIndex: index };
|
|
56
|
+
}
|
|
57
|
+
function buildCheckEntry(result, children = []) {
|
|
58
|
+
const errorString = result.error !== null ? result.error.message : null;
|
|
59
|
+
if (result.status === "skipped") {
|
|
60
|
+
return {
|
|
61
|
+
name: result.name,
|
|
62
|
+
status: result.status,
|
|
63
|
+
ok: result.ok,
|
|
64
|
+
severity: result.severity,
|
|
65
|
+
skipReason: result.skipReason,
|
|
66
|
+
detail: result.detail,
|
|
67
|
+
fix: result.fix,
|
|
68
|
+
error: errorString,
|
|
69
|
+
progress: result.progress,
|
|
70
|
+
durationMs: result.durationMs,
|
|
71
|
+
checks: children
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
name: result.name,
|
|
76
|
+
status: result.status,
|
|
77
|
+
ok: result.ok,
|
|
78
|
+
severity: result.severity,
|
|
79
|
+
skipReason: null,
|
|
80
|
+
detail: result.detail,
|
|
81
|
+
fix: result.fix,
|
|
82
|
+
error: errorString,
|
|
83
|
+
progress: result.progress,
|
|
84
|
+
durationMs: result.durationMs,
|
|
85
|
+
checks: children
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
export {
|
|
89
|
+
formatJsonReport
|
|
90
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type { ChecklistSummary, CheckOutcome, CheckReturnValue, FailedResult, FixLocation, FractionProgress, JsonCheckEntry, JsonChecklistEntry, JsonReport, PassedResult, PercentProgress, Progress, RdyCheck, RdyChecklist, RdyConfig, RdyKit, RdyReport, RdyResult, RdyStagedChecklist, ResolvedRdyConfig, Severity, SkippedResult, SkipResult, } from './types.ts';
|
|
2
|
+
export { isFlatChecklist, isPercentProgress } from './types.ts';
|
|
3
|
+
export { defineChecklists, defineRdyChecklist, defineRdyConfig, defineRdyKit, defineRdyStagedChecklist, } from './authoring.ts';
|
|
4
|
+
export { compareVersions, fileContains, fileDoesNotContain, fileExists, hasDevDependency, hasMinDevDependencyVersion, hasPackageJsonField, readFile, readPackageJson, } from './check-utils/index.ts';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { isFlatChecklist, isPercentProgress } from "./types.js";
|
|
2
|
+
import {
|
|
3
|
+
defineChecklists,
|
|
4
|
+
defineRdyChecklist,
|
|
5
|
+
defineRdyConfig,
|
|
6
|
+
defineRdyKit,
|
|
7
|
+
defineRdyStagedChecklist
|
|
8
|
+
} from "./authoring.js";
|
|
9
|
+
import {
|
|
10
|
+
compareVersions,
|
|
11
|
+
fileContains,
|
|
12
|
+
fileDoesNotContain,
|
|
13
|
+
fileExists,
|
|
14
|
+
hasDevDependency,
|
|
15
|
+
hasMinDevDependencyVersion,
|
|
16
|
+
hasPackageJsonField,
|
|
17
|
+
readFile,
|
|
18
|
+
readPackageJson
|
|
19
|
+
} from "./check-utils/index.js";
|
|
20
|
+
export {
|
|
21
|
+
compareVersions,
|
|
22
|
+
defineChecklists,
|
|
23
|
+
defineRdyChecklist,
|
|
24
|
+
defineRdyConfig,
|
|
25
|
+
defineRdyKit,
|
|
26
|
+
defineRdyStagedChecklist,
|
|
27
|
+
fileContains,
|
|
28
|
+
fileDoesNotContain,
|
|
29
|
+
fileExists,
|
|
30
|
+
hasDevDependency,
|
|
31
|
+
hasMinDevDependencyVersion,
|
|
32
|
+
hasPackageJsonField,
|
|
33
|
+
isFlatChecklist,
|
|
34
|
+
isPercentProgress,
|
|
35
|
+
readFile,
|
|
36
|
+
readPackageJson
|
|
37
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { printError, printStep, reportWriteResult } from "../terminal.js";
|
|
2
|
+
import { scaffoldConfig } from "./scaffold.js";
|
|
3
|
+
function initCommand({ dryRun, force }) {
|
|
4
|
+
if (dryRun) {
|
|
5
|
+
console.info("[dry-run mode]");
|
|
6
|
+
}
|
|
7
|
+
printStep("Scaffolding config");
|
|
8
|
+
let result;
|
|
9
|
+
try {
|
|
10
|
+
result = scaffoldConfig({ dryRun, force });
|
|
11
|
+
} catch (error) {
|
|
12
|
+
printError(`Failed to scaffold config: ${error instanceof Error ? error.message : String(error)}`);
|
|
13
|
+
return 1;
|
|
14
|
+
}
|
|
15
|
+
reportWriteResult(result.configResult, dryRun);
|
|
16
|
+
reportWriteResult(result.kitResult, dryRun);
|
|
17
|
+
if (result.configResult.outcome === "failed" || result.kitResult.outcome === "failed") {
|
|
18
|
+
return 1;
|
|
19
|
+
}
|
|
20
|
+
if (!dryRun) {
|
|
21
|
+
printStep("Next steps");
|
|
22
|
+
console.info(`
|
|
23
|
+
1. Customize .config/rdy.config.ts with your compile settings.
|
|
24
|
+
2. Add checklists to .rdy/kits/.
|
|
25
|
+
3. Test by running: npx readyup run
|
|
26
|
+
4. Commit the generated files.
|
|
27
|
+
`);
|
|
28
|
+
}
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
export {
|
|
32
|
+
initCommand
|
|
33
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { WriteResult } from '../writeFileWithCheck.ts';
|
|
2
|
+
interface ScaffoldOptions {
|
|
3
|
+
dryRun: boolean;
|
|
4
|
+
force: boolean;
|
|
5
|
+
}
|
|
6
|
+
interface ScaffoldResult {
|
|
7
|
+
configResult: WriteResult;
|
|
8
|
+
kitResult: WriteResult;
|
|
9
|
+
}
|
|
10
|
+
export declare function scaffoldConfig({ dryRun, force }: ScaffoldOptions): ScaffoldResult;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { writeFileWithCheck } from "../writeFileWithCheck.js";
|
|
2
|
+
import { rdyConfigTemplate, rdyKitTemplate } from "./templates.js";
|
|
3
|
+
const CONFIG_PATH = ".config/rdy.config.ts";
|
|
4
|
+
const KIT_PATH = ".rdy/kits/default.ts";
|
|
5
|
+
function scaffoldConfig({ dryRun, force }) {
|
|
6
|
+
const configResult = writeFileWithCheck(CONFIG_PATH, rdyConfigTemplate, { dryRun, overwrite: force });
|
|
7
|
+
const kitResult = writeFileWithCheck(KIT_PATH, rdyKitTemplate, {
|
|
8
|
+
dryRun,
|
|
9
|
+
overwrite: force
|
|
10
|
+
});
|
|
11
|
+
return { configResult, kitResult };
|
|
12
|
+
}
|
|
13
|
+
export {
|
|
14
|
+
scaffoldConfig
|
|
15
|
+
};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const rdyConfigTemplate = "import { defineRdyConfig } from 'readyup';\n\n/** Repo-level readyup settings. */\nexport default defineRdyConfig({\n compile: {\n srcDir: '.rdy/kits',\n outDir: '.rdy/kits',\n },\n});\n";
|
|
2
|
+
export declare const rdyKitTemplate = "import { defineRdyKit } from 'readyup';\n\n/**\n * Default rdy kit.\n *\n * Each checklist contains checks that run before a deployment or other operation.\n * Checks run concurrently within a checklist. Use `fix` to provide remediation hints.\n */\nexport default defineRdyKit({\n checklists: [\n {\n name: 'deploy',\n checks: [\n {\n name: 'environment variables set',\n check: () => Boolean(process.env['NODE_ENV']),\n fix: 'Set NODE_ENV before deploying',\n },\n ],\n },\n ],\n});\n";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const rdyConfigTemplate = `import { defineRdyConfig } from 'readyup';
|
|
2
|
+
|
|
3
|
+
/** Repo-level readyup settings. */
|
|
4
|
+
export default defineRdyConfig({
|
|
5
|
+
compile: {
|
|
6
|
+
srcDir: '.rdy/kits',
|
|
7
|
+
outDir: '.rdy/kits',
|
|
8
|
+
},
|
|
9
|
+
});
|
|
10
|
+
`;
|
|
11
|
+
const rdyKitTemplate = `import { defineRdyKit } from 'readyup';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Default rdy kit.
|
|
15
|
+
*
|
|
16
|
+
* Each checklist contains checks that run before a deployment or other operation.
|
|
17
|
+
* Checks run concurrently within a checklist. Use \`fix\` to provide remediation hints.
|
|
18
|
+
*/
|
|
19
|
+
export default defineRdyKit({
|
|
20
|
+
checklists: [
|
|
21
|
+
{
|
|
22
|
+
name: 'deploy',
|
|
23
|
+
checks: [
|
|
24
|
+
{
|
|
25
|
+
name: 'environment variables set',
|
|
26
|
+
check: () => Boolean(process.env['NODE_ENV']),
|
|
27
|
+
fix: 'Set NODE_ENV before deploying',
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
});
|
|
33
|
+
`;
|
|
34
|
+
export {
|
|
35
|
+
rdyConfigTemplate,
|
|
36
|
+
rdyKitTemplate
|
|
37
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function jitiImport(resolvedPath: string, moduleErrorDetail: string, exportNoun: string): Promise<Record<string, unknown>>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { isRecord } from "./isRecord.js";
|
|
2
|
+
async function jitiImport(resolvedPath, moduleErrorDetail, exportNoun) {
|
|
3
|
+
const { createJiti } = await import("jiti");
|
|
4
|
+
const jiti = createJiti(resolvedPath);
|
|
5
|
+
let imported;
|
|
6
|
+
try {
|
|
7
|
+
imported = await jiti.import(resolvedPath);
|
|
8
|
+
} catch (error) {
|
|
9
|
+
if (error instanceof Error && "code" in error && (error.code === "MODULE_NOT_FOUND" || error.code === "ERR_MODULE_NOT_FOUND")) {
|
|
10
|
+
const moduleMatch = error.message.match(/Cannot find (?:module|package) '([^']+)'/);
|
|
11
|
+
const moduleName = moduleMatch?.[1] ?? "unknown module";
|
|
12
|
+
throw new Error(`Cannot resolve '${moduleName}'. ${moduleErrorDetail}`);
|
|
13
|
+
}
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
16
|
+
if (!isRecord(imported)) {
|
|
17
|
+
throw new Error(`${exportNoun} must export an object, got ${Array.isArray(imported) ? "array" : typeof imported}`);
|
|
18
|
+
}
|
|
19
|
+
return imported;
|
|
20
|
+
}
|
|
21
|
+
export {
|
|
22
|
+
jitiImport
|
|
23
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { isRecord } from "./isRecord.js";
|
|
5
|
+
import { jitiImport } from "./jitiImport.js";
|
|
6
|
+
const DEFAULT_CONFIG = {
|
|
7
|
+
compile: {
|
|
8
|
+
srcDir: ".rdy/kits",
|
|
9
|
+
outDir: ".rdy/kits",
|
|
10
|
+
include: void 0
|
|
11
|
+
},
|
|
12
|
+
internal: {
|
|
13
|
+
dir: ".",
|
|
14
|
+
extension: ".ts"
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const LOOKUP_PATHS = [".config/rdy.config.ts"];
|
|
18
|
+
const RdyConfigSchema = z.looseObject({
|
|
19
|
+
compile: z.looseObject({
|
|
20
|
+
srcDir: z.string().optional(),
|
|
21
|
+
outDir: z.string().optional(),
|
|
22
|
+
include: z.string().optional()
|
|
23
|
+
}).optional(),
|
|
24
|
+
internal: z.looseObject({
|
|
25
|
+
dir: z.string().optional(),
|
|
26
|
+
extension: z.string().optional()
|
|
27
|
+
}).optional()
|
|
28
|
+
});
|
|
29
|
+
function assertIsRdyConfig(raw) {
|
|
30
|
+
RdyConfigSchema.parse(raw);
|
|
31
|
+
}
|
|
32
|
+
async function loadConfig(overridePath) {
|
|
33
|
+
let resolvedPath;
|
|
34
|
+
if (overridePath !== void 0) {
|
|
35
|
+
resolvedPath = path.resolve(process.cwd(), overridePath);
|
|
36
|
+
if (!existsSync(resolvedPath)) {
|
|
37
|
+
throw new Error(`Config not found: ${resolvedPath}`);
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
for (const lookupPath of LOOKUP_PATHS) {
|
|
41
|
+
const candidate = path.resolve(process.cwd(), lookupPath);
|
|
42
|
+
if (existsSync(candidate)) {
|
|
43
|
+
resolvedPath = candidate;
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (resolvedPath === void 0) {
|
|
49
|
+
return { ...DEFAULT_CONFIG };
|
|
50
|
+
}
|
|
51
|
+
const imported = await jitiImport(
|
|
52
|
+
resolvedPath,
|
|
53
|
+
"External packages imported by the config file must be installed in the project.",
|
|
54
|
+
"Config file"
|
|
55
|
+
);
|
|
56
|
+
const raw = imported.default !== void 0 && isRecord(imported.default) ? imported.default : imported;
|
|
57
|
+
assertIsRdyConfig(raw);
|
|
58
|
+
const compile = isRecord(raw.compile) ? raw.compile : void 0;
|
|
59
|
+
const internal = isRecord(raw.internal) ? raw.internal : void 0;
|
|
60
|
+
return {
|
|
61
|
+
compile: {
|
|
62
|
+
srcDir: typeof compile?.srcDir === "string" ? compile.srcDir : DEFAULT_CONFIG.compile.srcDir,
|
|
63
|
+
outDir: typeof compile?.outDir === "string" ? compile.outDir : DEFAULT_CONFIG.compile.outDir,
|
|
64
|
+
include: typeof compile?.include === "string" ? compile.include : void 0
|
|
65
|
+
},
|
|
66
|
+
internal: {
|
|
67
|
+
dir: typeof internal?.dir === "string" ? internal.dir : DEFAULT_CONFIG.internal.dir,
|
|
68
|
+
extension: typeof internal?.extension === "string" ? internal.extension : DEFAULT_CONFIG.internal.extension
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export {
|
|
73
|
+
loadConfig
|
|
74
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { assertIsPreflightCollection } from "./assertIsPreflightCollection.js";
|
|
6
|
+
import { isRecord } from "./isRecord.js";
|
|
7
|
+
import { resolveCollectionExports } from "./resolveCollectionExports.js";
|
|
8
|
+
import { validateCollection } from "./validateCollection.js";
|
|
9
|
+
async function loadRemoteCollection({ url, token }) {
|
|
10
|
+
const headers = {};
|
|
11
|
+
if (token !== void 0) {
|
|
12
|
+
headers.Authorization = `token ${token}`;
|
|
13
|
+
}
|
|
14
|
+
const response = await fetch(url, { headers });
|
|
15
|
+
if (!response.ok) {
|
|
16
|
+
throw new Error(`Failed to fetch remote collection from ${url}: ${response.status} ${response.statusText}`);
|
|
17
|
+
}
|
|
18
|
+
const body = await response.text();
|
|
19
|
+
const trimmedBody = body.trimStart().toLowerCase();
|
|
20
|
+
if (trimmedBody.startsWith("<html") || trimmedBody.startsWith("<!doctype")) {
|
|
21
|
+
throw new Error(`Remote collection URL returned an HTML page instead of JavaScript: ${url}`);
|
|
22
|
+
}
|
|
23
|
+
const tempDir = mkdtempSync(join(tmpdir(), "preflight-"));
|
|
24
|
+
const tempFile = join(tempDir, "collection.js");
|
|
25
|
+
try {
|
|
26
|
+
writeFileSync(tempFile, body, "utf8");
|
|
27
|
+
const fileUrl = `${pathToFileURL(tempFile).href}?t=${Date.now()}`;
|
|
28
|
+
const imported = await import(fileUrl);
|
|
29
|
+
const moduleRecord = isRecord(imported) ? imported : {};
|
|
30
|
+
const resolved = resolveCollectionExports(moduleRecord);
|
|
31
|
+
assertIsPreflightCollection(resolved);
|
|
32
|
+
validateCollection(resolved);
|
|
33
|
+
return resolved;
|
|
34
|
+
} finally {
|
|
35
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export {
|
|
39
|
+
loadRemoteCollection
|
|
40
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { assertIsRdyKit } from "./assertIsRdyKit.js";
|
|
6
|
+
import { isRecord } from "./isRecord.js";
|
|
7
|
+
import { resolveKitExports } from "./resolveKitExports.js";
|
|
8
|
+
import { validateKit } from "./validateKit.js";
|
|
9
|
+
async function loadRemoteKit({ url, token }) {
|
|
10
|
+
const headers = {};
|
|
11
|
+
if (token !== void 0) {
|
|
12
|
+
headers.Authorization = `token ${token}`;
|
|
13
|
+
}
|
|
14
|
+
const response = await fetch(url, { headers });
|
|
15
|
+
if (!response.ok) {
|
|
16
|
+
throw new Error(`Failed to fetch remote kit from ${url}: ${response.status} ${response.statusText}`);
|
|
17
|
+
}
|
|
18
|
+
const body = await response.text();
|
|
19
|
+
const trimmedBody = body.trimStart().toLowerCase();
|
|
20
|
+
if (trimmedBody.startsWith("<html") || trimmedBody.startsWith("<!doctype")) {
|
|
21
|
+
throw new Error(`Remote kit URL returned an HTML page instead of JavaScript: ${url}`);
|
|
22
|
+
}
|
|
23
|
+
const tempDir = mkdtempSync(join(tmpdir(), "rdy-"));
|
|
24
|
+
const tempFile = join(tempDir, "kit.js");
|
|
25
|
+
try {
|
|
26
|
+
writeFileSync(tempFile, body, "utf8");
|
|
27
|
+
const fileUrl = `${pathToFileURL(tempFile).href}?t=${Date.now()}`;
|
|
28
|
+
const imported = await import(fileUrl);
|
|
29
|
+
const moduleRecord = isRecord(imported) ? imported : {};
|
|
30
|
+
const resolved = resolveKitExports(moduleRecord);
|
|
31
|
+
assertIsRdyKit(resolved);
|
|
32
|
+
validateKit(resolved);
|
|
33
|
+
return resolved;
|
|
34
|
+
} finally {
|
|
35
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export {
|
|
39
|
+
loadRemoteKit
|
|
40
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface FlagDefinition {
|
|
2
|
+
long: string;
|
|
3
|
+
type: 'boolean' | 'string';
|
|
4
|
+
short?: string;
|
|
5
|
+
}
|
|
6
|
+
export type FlagSchema = Record<string, FlagDefinition>;
|
|
7
|
+
export type ParsedFlags<S extends FlagSchema> = {
|
|
8
|
+
[K in keyof S]: S[K]['type'] extends 'boolean' ? boolean : string | undefined;
|
|
9
|
+
};
|
|
10
|
+
export interface ParsedArgs<S extends FlagSchema> {
|
|
11
|
+
flags: ParsedFlags<S>;
|
|
12
|
+
positionals: string[];
|
|
13
|
+
}
|
|
14
|
+
export declare function parseArgs<S extends FlagSchema>(argv: string[], schema: S): ParsedArgs<S>;
|
|
15
|
+
export declare function translateParseError(error: unknown): string;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
function handleEqualsForm(arg, eqIndex, longToKey, definitions) {
|
|
2
|
+
const longFlag = arg.slice(0, eqIndex);
|
|
3
|
+
const key = longToKey.get(longFlag);
|
|
4
|
+
if (key === void 0) {
|
|
5
|
+
throw new Error(`unknown flag '${longFlag}'`);
|
|
6
|
+
}
|
|
7
|
+
const def = definitions.get(key);
|
|
8
|
+
if (def?.type === "boolean") {
|
|
9
|
+
throw new Error(`flag '${longFlag}' does not accept a value`);
|
|
10
|
+
}
|
|
11
|
+
const value = arg.slice(eqIndex + 1);
|
|
12
|
+
if (value === "") {
|
|
13
|
+
throw new Error(`${longFlag} requires a value`);
|
|
14
|
+
}
|
|
15
|
+
return { key, value };
|
|
16
|
+
}
|
|
17
|
+
function handleBareFlag(arg, index, argv, longToKey, shortToKey, definitions) {
|
|
18
|
+
const key = longToKey.get(arg) ?? shortToKey.get(arg);
|
|
19
|
+
if (key === void 0) {
|
|
20
|
+
throw new Error(`unknown flag '${arg}'`);
|
|
21
|
+
}
|
|
22
|
+
const def = definitions.get(key);
|
|
23
|
+
if (def?.type === "boolean") {
|
|
24
|
+
return { key, value: true, advance: 0 };
|
|
25
|
+
}
|
|
26
|
+
const next = argv[index + 1];
|
|
27
|
+
if (next === void 0 || next.startsWith("-") && next !== "-") {
|
|
28
|
+
throw new Error(`${def?.long} requires a value`);
|
|
29
|
+
}
|
|
30
|
+
return { key, value: next, advance: 1 };
|
|
31
|
+
}
|
|
32
|
+
function buildLookupTables(schema) {
|
|
33
|
+
const longToKey = /* @__PURE__ */ new Map();
|
|
34
|
+
const shortToKey = /* @__PURE__ */ new Map();
|
|
35
|
+
const definitions = /* @__PURE__ */ new Map();
|
|
36
|
+
for (const [key, def] of Object.entries(schema)) {
|
|
37
|
+
longToKey.set(def.long, key);
|
|
38
|
+
definitions.set(key, def);
|
|
39
|
+
if (def.short !== void 0) {
|
|
40
|
+
shortToKey.set(def.short, key);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return { longToKey, shortToKey, definitions };
|
|
44
|
+
}
|
|
45
|
+
function parseArgsInternal(argv, schema) {
|
|
46
|
+
const { longToKey, shortToKey, definitions } = buildLookupTables(schema);
|
|
47
|
+
const flags = {};
|
|
48
|
+
for (const [key, def] of Object.entries(schema)) {
|
|
49
|
+
flags[key] = def.type === "boolean" ? false : void 0;
|
|
50
|
+
}
|
|
51
|
+
const positionals = [];
|
|
52
|
+
let pastDelimiter = false;
|
|
53
|
+
for (let i = 0; i < argv.length; i++) {
|
|
54
|
+
const arg = argv[i] ?? "";
|
|
55
|
+
if (pastDelimiter) {
|
|
56
|
+
positionals.push(arg);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (arg === "--") {
|
|
60
|
+
pastDelimiter = true;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const eqIndex = arg.indexOf("=");
|
|
64
|
+
if (eqIndex !== -1 && arg.startsWith("--")) {
|
|
65
|
+
const { key, value } = handleEqualsForm(arg, eqIndex, longToKey, definitions);
|
|
66
|
+
flags[key] = value;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (arg.startsWith("-") && arg !== "-") {
|
|
70
|
+
const { key, value, advance } = handleBareFlag(arg, i, argv, longToKey, shortToKey, definitions);
|
|
71
|
+
flags[key] = value;
|
|
72
|
+
i += advance;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
positionals.push(arg);
|
|
76
|
+
}
|
|
77
|
+
return { flags, positionals };
|
|
78
|
+
}
|
|
79
|
+
function parseArgs(argv, schema) {
|
|
80
|
+
return parseArgsInternal(argv, schema);
|
|
81
|
+
}
|
|
82
|
+
function translateParseError(error) {
|
|
83
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
84
|
+
const flagMatch = message.match(/^unknown flag '(.+)'$/);
|
|
85
|
+
if (flagMatch?.[1] !== void 0) {
|
|
86
|
+
return `Unknown option: ${flagMatch[1]}`;
|
|
87
|
+
}
|
|
88
|
+
return message;
|
|
89
|
+
}
|
|
90
|
+
export {
|
|
91
|
+
parseArgs,
|
|
92
|
+
translateParseError
|
|
93
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FixLocation, PreflightReport, Severity } from './types.ts';
|
|
2
|
+
export interface ReportPreflightOptions {
|
|
3
|
+
fixLocation?: FixLocation;
|
|
4
|
+
reportOn?: Severity;
|
|
5
|
+
}
|
|
6
|
+
export declare function formatSummaryCounts(passed: number, failed: number, skipped: number): string;
|
|
7
|
+
export declare function reportPreflight(report: PreflightReport, options?: ReportPreflightOptions): string;
|