@canonical/pragma-cli 0.24.0 → 0.26.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/dist/pragma +0 -0
- package/package.json +12 -12
- package/src/config/readConfig.test.ts +152 -0
- package/src/config/readConfig.ts +63 -1
- package/src/config/types.ts +9 -0
- package/src/config/writeConfig.ts +6 -0
- package/src/domains/doctor/operations/checks/checkPackageRefs.ts +76 -0
- package/src/domains/doctor/operations/checks/index.ts +1 -0
- package/src/domains/doctor/operations/runChecks.test.ts +11 -5
- package/src/domains/doctor/operations/runChecks.ts +2 -0
- package/src/domains/info/formatters/info.test.ts +34 -0
- package/src/domains/info/formatters/info.ts +15 -0
- package/src/domains/info/operations/collectInfo.ts +30 -1
- package/src/domains/info/types.ts +8 -0
- package/src/domains/refs/commands/updateRefs.ts +52 -0
- package/src/domains/refs/formatters/updateRefs.ts +27 -0
- package/src/domains/refs/index.ts +27 -0
- package/src/domains/refs/operations/gitOps.test.ts +155 -0
- package/src/domains/refs/operations/gitOps.ts +92 -0
- package/src/domains/refs/operations/index.ts +10 -0
- package/src/domains/refs/operations/parseRef.test.ts +157 -0
- package/src/domains/refs/operations/parseRef.ts +114 -0
- package/src/domains/refs/operations/paths.test.ts +98 -0
- package/src/domains/refs/operations/paths.ts +63 -0
- package/src/domains/refs/operations/readGlobalRefs.test.ts +63 -0
- package/src/domains/refs/operations/readGlobalRefs.ts +37 -0
- package/src/domains/refs/operations/updateRefs.test.ts +164 -0
- package/src/domains/refs/operations/updateRefs.ts +215 -0
- package/src/domains/shared/bootStore.ts +31 -15
- package/src/domains/shared/packages.ts +99 -49
- package/src/domains/shared/runtime.ts +73 -3
- package/src/domains/skill/helpers/resolveSkillSources.ts +16 -12
- package/src/pipeline/resolveCommandKind.ts +1 -1
- package/src/pipeline/runCli.ts +2 -1
- package/src/testing/integration/boot.test.ts +38 -0
|
@@ -7,27 +7,43 @@
|
|
|
7
7
|
* @note Impure — reads filesystem, creates ke store.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { existsSync } from "node:fs";
|
|
10
11
|
import { join } from "node:path";
|
|
11
12
|
import type { SourceSpec, Store } from "@canonical/ke";
|
|
12
13
|
import { createStore } from "@canonical/ke";
|
|
13
|
-
import type { PragmaConfig } from "#config";
|
|
14
14
|
import { PragmaError } from "../../error/index.js";
|
|
15
|
-
import {
|
|
15
|
+
import type { PackageRef } from "../refs/operations/parseRef.js";
|
|
16
|
+
import { resolvePackages } from "./packages.js";
|
|
16
17
|
import { PREFIX_MAP } from "./prefixes.js";
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
20
|
+
* Convention-based TTL directories to scan in each package.
|
|
21
|
+
* Only directories that exist are globbed — packages that lack
|
|
22
|
+
* a `data/` or `definitions/` dir are silently skipped.
|
|
21
23
|
*/
|
|
22
|
-
|
|
24
|
+
const TTL_DIRS: readonly { dir: string; glob: string }[] = [
|
|
25
|
+
{ dir: "definitions", glob: "definitions/**/*.ttl" },
|
|
26
|
+
{ dir: "data", glob: "data/**/*.ttl" },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Resolve default TTL sources from resolved packages.
|
|
31
|
+
*
|
|
32
|
+
* Uses convention-based TTL discovery: definitions and data globs
|
|
33
|
+
* relative to each package root. Only adds globs for directories
|
|
34
|
+
* that actually exist in the package.
|
|
35
|
+
*
|
|
36
|
+
* @param refs - Parsed package references. Omit for defaults.
|
|
37
|
+
*/
|
|
38
|
+
export function defaultSources(refs?: ReadonlyArray<PackageRef>): SourceSpec[] {
|
|
23
39
|
const sources: SourceSpec[] = [];
|
|
24
|
-
const resolved = resolvePackages();
|
|
40
|
+
const resolved = resolvePackages(refs);
|
|
25
41
|
|
|
26
|
-
for (const {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
42
|
+
for (const { dir } of resolved) {
|
|
43
|
+
for (const entry of TTL_DIRS) {
|
|
44
|
+
if (existsSync(join(dir, entry.dir))) {
|
|
45
|
+
sources.push(join(dir, entry.glob));
|
|
46
|
+
}
|
|
31
47
|
}
|
|
32
48
|
}
|
|
33
49
|
|
|
@@ -35,14 +51,14 @@ export function defaultSources(): SourceSpec[] {
|
|
|
35
51
|
}
|
|
36
52
|
|
|
37
53
|
export interface BootStoreOptions {
|
|
38
|
-
/** Override
|
|
39
|
-
config?: PragmaConfig;
|
|
40
|
-
/** Override sources (skip config sources field). */
|
|
54
|
+
/** Override sources (skip filesystem resolution). */
|
|
41
55
|
sources?: SourceSpec[];
|
|
42
56
|
/** Working directory for resolving relative paths. */
|
|
43
57
|
cwd?: string;
|
|
44
58
|
/** Cache path for serialized store. */
|
|
45
59
|
cache?: string;
|
|
60
|
+
/** Parsed package references for ref-based resolution. */
|
|
61
|
+
refs?: ReadonlyArray<PackageRef>;
|
|
46
62
|
}
|
|
47
63
|
|
|
48
64
|
/**
|
|
@@ -53,7 +69,7 @@ export interface BootStoreOptions {
|
|
|
53
69
|
export async function bootStore(
|
|
54
70
|
options: BootStoreOptions = {},
|
|
55
71
|
): Promise<Store> {
|
|
56
|
-
const sources = options.sources ?? defaultSources();
|
|
72
|
+
const sources = options.sources ?? defaultSources(options.refs);
|
|
57
73
|
|
|
58
74
|
try {
|
|
59
75
|
const store = await createStore({
|
|
@@ -1,53 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Design system package registry.
|
|
2
|
+
* Design system package registry and resolution.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Resolves semantic packages from three sources (in priority order):
|
|
5
|
+
* 1. file:// local path (development)
|
|
6
|
+
* 2. git ref cache (shared/CI)
|
|
7
|
+
* 3. require.resolve from node_modules (npm fallback)
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* - `ttl` — glob patterns for TTL data, relative to the package root
|
|
11
|
-
* - `skills` — subpath to the skills directory, or `undefined` if none
|
|
9
|
+
* The package list itself is configurable via pragma.config.json `packages`
|
|
10
|
+
* field. When absent, the hardcoded DEFAULT_PACKAGES are used.
|
|
12
11
|
*/
|
|
13
12
|
|
|
13
|
+
import { existsSync } from "node:fs";
|
|
14
14
|
import { createRequire } from "node:module";
|
|
15
15
|
import { dirname } from "node:path";
|
|
16
|
+
import type { PackageRef } from "../refs/operations/parseRef.js";
|
|
17
|
+
import { gitCacheDir } from "../refs/operations/paths.js";
|
|
16
18
|
|
|
17
19
|
const require = createRequire(import.meta.url);
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
/** Glob patterns for TTL data (relative to package root). */
|
|
23
|
-
readonly ttl: readonly string[];
|
|
24
|
-
/** Subpath to skills directory, or undefined if the package has no skills. */
|
|
25
|
-
readonly skills: string | undefined;
|
|
26
|
-
}
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Default package registry (used when config.packages is absent)
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
27
24
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
{
|
|
35
|
-
pkg: "@canonical/anatomy-dsl",
|
|
36
|
-
ttl: ["definitions/**/*.ttl"],
|
|
37
|
-
skills: undefined,
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
pkg: "@canonical/code-standards",
|
|
41
|
-
ttl: ["definitions/**/*.ttl", "data/**/*.ttl"],
|
|
42
|
-
skills: "skills",
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
pkg: "@canonical/pragma-cli",
|
|
46
|
-
ttl: [],
|
|
47
|
-
skills: "skills",
|
|
48
|
-
},
|
|
25
|
+
/** Default semantic packages loaded when no config overrides are present. */
|
|
26
|
+
export const DEFAULT_PACKAGES: readonly string[] = [
|
|
27
|
+
"@canonical/design-system",
|
|
28
|
+
"@canonical/anatomy-dsl",
|
|
29
|
+
"@canonical/code-standards",
|
|
30
|
+
"@canonical/pragma-cli",
|
|
49
31
|
];
|
|
50
32
|
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// Types
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
|
|
51
37
|
export interface ResolvedPackage {
|
|
52
38
|
/** npm package name. */
|
|
53
39
|
readonly pkg: string;
|
|
@@ -55,22 +41,86 @@ export interface ResolvedPackage {
|
|
|
55
41
|
readonly dir: string;
|
|
56
42
|
}
|
|
57
43
|
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Resolution
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
58
48
|
/**
|
|
59
|
-
* Resolve
|
|
60
|
-
*
|
|
61
|
-
*
|
|
49
|
+
* Resolve packages to filesystem directories.
|
|
50
|
+
*
|
|
51
|
+
* When `refs` is provided, each PackageRef is resolved according to its kind.
|
|
52
|
+
* When `refs` is omitted, the DEFAULT_PACKAGES are resolved via require.resolve.
|
|
53
|
+
*
|
|
54
|
+
* @param refs - Parsed package references (from config merge). Omit for defaults.
|
|
55
|
+
* @returns Array of resolved packages with absolute directory paths.
|
|
62
56
|
*/
|
|
63
|
-
export function resolvePackages(
|
|
57
|
+
export function resolvePackages(
|
|
58
|
+
refs?: ReadonlyArray<PackageRef>,
|
|
59
|
+
): ResolvedPackage[] {
|
|
60
|
+
if (!refs) {
|
|
61
|
+
return resolveNpmPackages(DEFAULT_PACKAGES);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
64
|
const resolved: ResolvedPackage[] = [];
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
65
|
+
|
|
66
|
+
for (const ref of refs) {
|
|
67
|
+
switch (ref.kind) {
|
|
68
|
+
case "file": {
|
|
69
|
+
if (!existsSync(ref.path)) {
|
|
70
|
+
console.warn(
|
|
71
|
+
`pragma: package "${ref.pkg}" points to ${ref.path} which does not exist. Check the path or remove the file:// source.`,
|
|
72
|
+
);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
resolved.push({ pkg: ref.pkg, dir: ref.path });
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
case "git": {
|
|
80
|
+
const cacheDir = gitCacheDir(ref.pkg, ref.ref);
|
|
81
|
+
if (existsSync(cacheDir)) {
|
|
82
|
+
resolved.push({ pkg: ref.pkg, dir: cacheDir });
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
// Cache miss — warn and fall through to npm
|
|
86
|
+
console.warn(
|
|
87
|
+
`pragma: git ref for "${ref.pkg}" not cached. Run "pragma update-refs" to fetch it.`,
|
|
88
|
+
);
|
|
89
|
+
resolveNpmFallback(ref.pkg, resolved);
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
case "npm": {
|
|
94
|
+
resolveNpmFallback(ref.pkg, resolved);
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
73
97
|
}
|
|
74
98
|
}
|
|
99
|
+
|
|
75
100
|
return resolved;
|
|
76
101
|
}
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Helpers
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
/** Resolve a list of package names via require.resolve. */
|
|
108
|
+
function resolveNpmPackages(pkgs: readonly string[]): ResolvedPackage[] {
|
|
109
|
+
const resolved: ResolvedPackage[] = [];
|
|
110
|
+
for (const pkg of pkgs) {
|
|
111
|
+
resolveNpmFallback(pkg, resolved);
|
|
112
|
+
}
|
|
113
|
+
return resolved;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Try to resolve a single package via require.resolve, push if found. */
|
|
117
|
+
function resolveNpmFallback(pkg: string, resolved: ResolvedPackage[]): void {
|
|
118
|
+
try {
|
|
119
|
+
resolved.push({
|
|
120
|
+
pkg,
|
|
121
|
+
dir: dirname(require.resolve(`${pkg}/package.json`)),
|
|
122
|
+
});
|
|
123
|
+
} catch {
|
|
124
|
+
// package not installed — skip
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -10,7 +10,14 @@
|
|
|
10
10
|
|
|
11
11
|
import type { SourceSpec } from "@canonical/ke";
|
|
12
12
|
import { readConfig } from "#config";
|
|
13
|
+
import {
|
|
14
|
+
type PackageRef,
|
|
15
|
+
parsePackageEntry,
|
|
16
|
+
type RawPackageEntry,
|
|
17
|
+
} from "../refs/operations/parseRef.js";
|
|
18
|
+
import readGlobalRefs from "../refs/operations/readGlobalRefs.js";
|
|
13
19
|
import { bootStore } from "./bootStore.js";
|
|
20
|
+
import { DEFAULT_PACKAGES } from "./packages.js";
|
|
14
21
|
import type { PragmaRuntime } from "./types/index.js";
|
|
15
22
|
|
|
16
23
|
export type { PragmaRuntime } from "./types/index.js";
|
|
@@ -18,9 +25,11 @@ export type { PragmaRuntime } from "./types/index.js";
|
|
|
18
25
|
/**
|
|
19
26
|
* Create a fully initialized `PragmaRuntime`.
|
|
20
27
|
*
|
|
21
|
-
* Reads config from `cwd` (defaults to `process.cwd()`),
|
|
22
|
-
*
|
|
23
|
-
*
|
|
28
|
+
* Reads config from `cwd` (defaults to `process.cwd()`), merges global
|
|
29
|
+
* and project package refs, boots the ke store from resolved sources,
|
|
30
|
+
* and returns a runtime ready for operation calls.
|
|
31
|
+
*
|
|
32
|
+
* The caller owns the lifecycle and must call `dispose()`.
|
|
24
33
|
*
|
|
25
34
|
* @param options.cwd - Working directory for config and source resolution.
|
|
26
35
|
* @param options.sources - Override sources for testing (skip filesystem resolution).
|
|
@@ -33,9 +42,16 @@ export async function bootPragma(options?: {
|
|
|
33
42
|
}): Promise<PragmaRuntime> {
|
|
34
43
|
const cwd = options?.cwd ?? process.cwd();
|
|
35
44
|
const config = readConfig(cwd);
|
|
45
|
+
|
|
46
|
+
// Merge package refs: project overrides global, both override defaults.
|
|
47
|
+
const refs = options?.sources
|
|
48
|
+
? undefined
|
|
49
|
+
: mergeAndParseRefs(config.packages);
|
|
50
|
+
|
|
36
51
|
const store = await bootStore({
|
|
37
52
|
cwd,
|
|
38
53
|
sources: options?.sources,
|
|
54
|
+
refs,
|
|
39
55
|
});
|
|
40
56
|
|
|
41
57
|
return {
|
|
@@ -45,3 +61,57 @@ export async function bootPragma(options?: {
|
|
|
45
61
|
dispose: () => store.dispose(),
|
|
46
62
|
};
|
|
47
63
|
}
|
|
64
|
+
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Ref merging
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Merge global refs, project config packages, and defaults into a final
|
|
71
|
+
* parsed PackageRef array.
|
|
72
|
+
*
|
|
73
|
+
* Priority: project packages > global refs > hardcoded defaults.
|
|
74
|
+
* Merging is by package name — a project entry for "@canonical/foo"
|
|
75
|
+
* overrides the global entry for the same package.
|
|
76
|
+
*/
|
|
77
|
+
function mergeAndParseRefs(
|
|
78
|
+
projectPackages?: ReadonlyArray<RawPackageEntry>,
|
|
79
|
+
): PackageRef[] {
|
|
80
|
+
const globalEntries = readGlobalRefs();
|
|
81
|
+
|
|
82
|
+
// If neither project nor global defines packages, return undefined
|
|
83
|
+
// so resolvePackages() uses hardcoded defaults.
|
|
84
|
+
if (
|
|
85
|
+
(!projectPackages || projectPackages.length === 0) &&
|
|
86
|
+
globalEntries.length === 0
|
|
87
|
+
) {
|
|
88
|
+
return DEFAULT_PACKAGES.map((pkg) => parsePackageEntry(pkg));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Build a name → entry map, global first, project overrides.
|
|
92
|
+
const merged = new Map<string, RawPackageEntry>();
|
|
93
|
+
|
|
94
|
+
// Start with defaults (lowest priority)
|
|
95
|
+
for (const pkg of DEFAULT_PACKAGES) {
|
|
96
|
+
merged.set(pkg, pkg);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Global overrides defaults
|
|
100
|
+
for (const entry of globalEntries) {
|
|
101
|
+
const name = typeof entry === "string" ? entry : entry.name;
|
|
102
|
+
merged.set(name, entry);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Project overrides global
|
|
106
|
+
if (projectPackages) {
|
|
107
|
+
// When project explicitly declares packages, it's a full replacement
|
|
108
|
+
// of the package list — clear defaults and global, use project only.
|
|
109
|
+
merged.clear();
|
|
110
|
+
for (const entry of projectPackages) {
|
|
111
|
+
const name = typeof entry === "string" ? entry : entry.name;
|
|
112
|
+
merged.set(name, entry);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return [...merged.values()].map(parsePackageEntry);
|
|
117
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
1
2
|
import { join } from "node:path";
|
|
2
|
-
import {
|
|
3
|
+
import type { PackageRef } from "../../refs/operations/parseRef.js";
|
|
4
|
+
import { resolvePackages } from "../../shared/packages.js";
|
|
3
5
|
|
|
4
6
|
/** A resolved skill source directory from an installed package. */
|
|
5
7
|
export interface SkillSource {
|
|
@@ -10,24 +12,26 @@ export interface SkillSource {
|
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
|
-
* Resolve skill source directories from
|
|
14
|
-
* Package-manager agnostic -- works with bun, npm, pnpm, yarn.
|
|
15
|
+
* Resolve skill source directories from resolved packages.
|
|
15
16
|
*
|
|
17
|
+
* Auto-detects skills by checking for a `skills/` subdirectory
|
|
18
|
+
* in each resolved package root.
|
|
19
|
+
*
|
|
20
|
+
* @param refs - Parsed package references. Omit for defaults.
|
|
16
21
|
* @returns Array of resolved skill sources with absolute paths.
|
|
17
22
|
* @note Impure
|
|
18
23
|
*/
|
|
19
|
-
export default function resolveSkillSources(
|
|
24
|
+
export default function resolveSkillSources(
|
|
25
|
+
refs?: ReadonlyArray<PackageRef>,
|
|
26
|
+
): SkillSource[] {
|
|
20
27
|
const sources: SkillSource[] = [];
|
|
21
|
-
const resolved = resolvePackages();
|
|
28
|
+
const resolved = resolvePackages(refs);
|
|
22
29
|
|
|
23
30
|
for (const { pkg, dir } of resolved) {
|
|
24
|
-
const
|
|
25
|
-
if (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
dir: join(dir, def.skills),
|
|
29
|
-
packageName: pkg,
|
|
30
|
-
});
|
|
31
|
+
const skillsDir = join(dir, "skills");
|
|
32
|
+
if (existsSync(skillsDir)) {
|
|
33
|
+
sources.push({ dir: skillsDir, packageName: pkg });
|
|
34
|
+
}
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
return sources;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import type { CommandKind } from "./types.js";
|
|
9
9
|
|
|
10
10
|
/** Commands that do not require the ke store to be booted. */
|
|
11
|
-
const STORE_SKIP_COMMANDS = new Set(["setup", "mcp"]);
|
|
11
|
+
const STORE_SKIP_COMMANDS = new Set(["setup", "mcp", "update-refs"]);
|
|
12
12
|
|
|
13
13
|
function findCommandArg(argv: readonly string[]): string | undefined {
|
|
14
14
|
const args = argv.slice(2);
|
package/src/pipeline/runCli.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { GlobalFlags } from "@canonical/cli-core";
|
|
2
2
|
import { CommanderError } from "commander";
|
|
3
|
+
import { commands as refsCommands } from "../domains/refs/index.js";
|
|
3
4
|
import { commands as setupCommands } from "../domains/setup/index.js";
|
|
4
5
|
import type { PragmaContext } from "../domains/shared/context.js";
|
|
5
6
|
import type { PragmaRuntime } from "../domains/shared/runtime.js";
|
|
@@ -140,7 +141,7 @@ async function runStoreSkip(
|
|
|
140
141
|
globalFlags,
|
|
141
142
|
interactive: runInteractiveCommand,
|
|
142
143
|
};
|
|
143
|
-
const commands = [...setupCommands()];
|
|
144
|
+
const commands = [...setupCommands(), ...refsCommands()];
|
|
144
145
|
const program = createProgram(commands, stubCtx);
|
|
145
146
|
|
|
146
147
|
try {
|
|
@@ -5,9 +5,12 @@
|
|
|
5
5
|
* consistency from the canonical fixture.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { mkdtempSync } from "node:fs";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
8
10
|
import { afterAll, describe, expect, it } from "vitest";
|
|
9
11
|
import { listBlocks } from "../../domains/block/operations/index.js";
|
|
10
12
|
import type { PragmaRuntime } from "../../domains/shared/runtime.js";
|
|
13
|
+
import { bootPragma } from "../../domains/shared/runtime.js";
|
|
11
14
|
import createTestRuntime from "../helpers/createTestRuntime.js";
|
|
12
15
|
|
|
13
16
|
describe("PragmaRuntime boot", () => {
|
|
@@ -54,6 +57,41 @@ describe("PragmaRuntime boot", () => {
|
|
|
54
57
|
expect(names).toContain("Button");
|
|
55
58
|
});
|
|
56
59
|
|
|
60
|
+
it("boots with file:// package ref", async () => {
|
|
61
|
+
// Create a temp "package" with TTL in the convention structure
|
|
62
|
+
const { mkdirSync, copyFileSync, writeFileSync } = await import("node:fs");
|
|
63
|
+
const { join } = await import("node:path");
|
|
64
|
+
|
|
65
|
+
const fixturesDir = new URL("../fixtures/", import.meta.url).pathname;
|
|
66
|
+
const tmpPkg = mkdtempSync(join(tmpdir(), "pragma-ref-pkg-"));
|
|
67
|
+
const dataDir = join(tmpPkg, "data");
|
|
68
|
+
mkdirSync(dataDir, { recursive: true });
|
|
69
|
+
copyFileSync(
|
|
70
|
+
join(fixturesDir, "canonical.ttl"),
|
|
71
|
+
join(dataDir, "canonical.ttl"),
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// Create a temp project dir with config pointing to the file:// package
|
|
75
|
+
const tmpProject = mkdtempSync(join(tmpdir(), "pragma-ref-project-"));
|
|
76
|
+
writeFileSync(
|
|
77
|
+
join(tmpProject, "pragma.config.json"),
|
|
78
|
+
JSON.stringify({
|
|
79
|
+
packages: [{ name: "@test/fixture", source: `file://${tmpPkg}` }],
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const rt = await bootPragma({ cwd: tmpProject });
|
|
84
|
+
runtimes.push(rt);
|
|
85
|
+
|
|
86
|
+
expect(rt.store).toBeDefined();
|
|
87
|
+
expect(rt.config.packages).toHaveLength(1);
|
|
88
|
+
|
|
89
|
+
// Clean up
|
|
90
|
+
const { rmSync } = await import("node:fs");
|
|
91
|
+
rmSync(tmpPkg, { recursive: true, force: true });
|
|
92
|
+
rmSync(tmpProject, { recursive: true, force: true });
|
|
93
|
+
});
|
|
94
|
+
|
|
57
95
|
it("two runtimes produce identical store content", async () => {
|
|
58
96
|
const rt1 = await createTestRuntime();
|
|
59
97
|
const rt2 = await createTestRuntime();
|