@contextforge/core 0.1.1 → 0.1.2

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/index.d.ts CHANGED
@@ -182,7 +182,7 @@ declare function hasScript(packageJson: PackageJson | null, scriptName: string):
182
182
 
183
183
  declare function findPack(registry: LoadedPack[], packName: string): LoadedPack | undefined;
184
184
  declare function resolvePacks(packNames: string[], registry: LoadedPack[]): LoadedPack[];
185
- declare function recommendPacks(analysis: ProjectAnalysis, registry: LoadedPack[]): LoadedPack[];
185
+ declare function recommendPacks(analysis: ProjectAnalysis, registry: LoadedPack[]): Promise<LoadedPack[]>;
186
186
  declare function packMatchesProject(pack: LoadedPack, root: string, packageJson: PackageJson | null): Promise<boolean>;
187
187
  declare function packageManagerLabel(packageManager: PackageManager): string;
188
188
 
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/detect/detectProject.ts
2
- import path5 from "path";
2
+ import path6 from "path";
3
3
  import fg4 from "fast-glob";
4
- import fs5 from "fs-extra";
4
+ import fs6 from "fs-extra";
5
5
 
6
6
  // src/detect/detectAITools.ts
7
7
  import path from "path";
@@ -91,6 +91,28 @@ async function detectPackageManager(root) {
91
91
  return "unknown";
92
92
  }
93
93
 
94
+ // src/project/packageJson.ts
95
+ import path5 from "path";
96
+ import fs5 from "fs-extra";
97
+ async function readPackageJson(root) {
98
+ const packageJsonPath = path5.join(root, "package.json");
99
+ if (!await fs5.pathExists(packageJsonPath)) {
100
+ return null;
101
+ }
102
+ return fs5.readJson(packageJsonPath);
103
+ }
104
+ function hasPackage(packageJson, packageName) {
105
+ if (!packageJson) {
106
+ return false;
107
+ }
108
+ return Boolean(
109
+ packageJson.dependencies?.[packageName] || packageJson.devDependencies?.[packageName] || packageJson.peerDependencies?.[packageName] || packageJson.optionalDependencies?.[packageName]
110
+ );
111
+ }
112
+ function hasScript(packageJson, scriptName) {
113
+ return Boolean(packageJson?.scripts?.[scriptName]);
114
+ }
115
+
94
116
  // src/detect/detectProject.ts
95
117
  async function hasAny(root, patterns) {
96
118
  const matches = await fg4(patterns, {
@@ -101,21 +123,27 @@ async function hasAny(root, patterns) {
101
123
  return matches.length > 0;
102
124
  }
103
125
  async function detectProject(root) {
104
- const resolvedRoot = path5.resolve(root);
105
- const [packageManager, framework, database, aiTools] = await Promise.all([
126
+ const resolvedRoot = path6.resolve(root);
127
+ const [packageManager, framework, database, aiTools, packageJson] = await Promise.all([
106
128
  detectPackageManager(resolvedRoot),
107
129
  detectFramework(resolvedRoot),
108
130
  detectDatabase(resolvedRoot),
109
- detectAITools(resolvedRoot)
131
+ detectAITools(resolvedRoot),
132
+ readPackageJson(resolvedRoot)
110
133
  ]);
111
- const [typescript, tailwind, shadcn, vitest, jest, playwright] = await Promise.all([
112
- fs5.pathExists(path5.join(resolvedRoot, "tsconfig.json")),
134
+ const [typescript, tailwindConfig, componentsJson, vitestConfig, jestConfig, playwrightConfig] = await Promise.all([
135
+ fs6.pathExists(path6.join(resolvedRoot, "tsconfig.json")),
113
136
  hasAny(resolvedRoot, ["tailwind.config.{js,ts,mjs,mts,cjs,cts}"]),
114
- fs5.pathExists(path5.join(resolvedRoot, "components.json")),
137
+ fs6.pathExists(path6.join(resolvedRoot, "components.json")),
115
138
  hasAny(resolvedRoot, ["vitest.config.{js,ts,mjs,mts,cjs,cts}"]),
116
139
  hasAny(resolvedRoot, ["jest.config.{js,ts,mjs,mts,cjs,cts}", "jest.config.json"]),
117
140
  hasAny(resolvedRoot, ["playwright.config.{js,ts,mjs,mts,cjs,cts}"])
118
141
  ]);
142
+ const tailwind = tailwindConfig || hasPackage(packageJson, "tailwindcss");
143
+ const shadcn = componentsJson || hasPackage(packageJson, "shadcn") || hasPackage(packageJson, "shadcn-ui");
144
+ const vitest = vitestConfig || hasPackage(packageJson, "vitest");
145
+ const jest = jestConfig || hasPackage(packageJson, "jest");
146
+ const playwright = playwrightConfig || hasPackage(packageJson, "@playwright/test") || hasPackage(packageJson, "playwright");
119
147
  return {
120
148
  root: resolvedRoot,
121
149
  packageManager,
@@ -176,30 +204,30 @@ var RemoteRegistryIndexSchema = z.object({
176
204
  });
177
205
 
178
206
  // src/registry/loadRegistry.ts
179
- import path7 from "path";
207
+ import path8 from "path";
180
208
 
181
209
  // src/registry/localRegistry.ts
182
- import path6 from "path";
183
- import fs6 from "fs-extra";
210
+ import path7 from "path";
211
+ import fs7 from "fs-extra";
184
212
  async function loadLocalRegistry(registryRoot, source) {
185
- if (!await fs6.pathExists(registryRoot)) {
213
+ if (!await fs7.pathExists(registryRoot)) {
186
214
  return [];
187
215
  }
188
- const entries = await fs6.readdir(registryRoot, { withFileTypes: true });
216
+ const entries = await fs7.readdir(registryRoot, { withFileTypes: true });
189
217
  const packs = [];
190
218
  for (const entry of entries) {
191
219
  if (!entry.isDirectory()) {
192
220
  continue;
193
221
  }
194
- const directory = path6.join(registryRoot, entry.name);
195
- const packPath = path6.join(directory, "pack.json");
196
- if (!await fs6.pathExists(packPath)) {
222
+ const directory = path7.join(registryRoot, entry.name);
223
+ const packPath = path7.join(directory, "pack.json");
224
+ if (!await fs7.pathExists(packPath)) {
197
225
  continue;
198
226
  }
199
- const parsed = PackSchema.parse(await fs6.readJson(packPath));
227
+ const parsed = PackSchema.parse(await fs7.readJson(packPath));
200
228
  const readOptional = async (fileName) => {
201
- const filePath = path6.join(directory, fileName);
202
- return await fs6.pathExists(filePath) ? fs6.readFile(filePath, "utf8") : void 0;
229
+ const filePath = path7.join(directory, fileName);
230
+ return await fs7.pathExists(filePath) ? fs7.readFile(filePath, "utf8") : void 0;
203
231
  };
204
232
  const rules = await readOptional("rules.md");
205
233
  if (!rules) {
@@ -325,7 +353,7 @@ async function loadRegistry(input = {}) {
325
353
  const sources = input.sources ?? DEFAULT_REGISTRY_SOURCES;
326
354
  const packs = [];
327
355
  if (input.root) {
328
- packs.push(...await loadLocalRegistry(path7.join(input.root, PROJECT_PACK_CACHE), "project-cache"));
356
+ packs.push(...await loadLocalRegistry(path8.join(input.root, PROJECT_PACK_CACHE), "project-cache"));
329
357
  }
330
358
  for (const source of sources) {
331
359
  packs.push(...await loadRegistrySource(source, input.timeoutMs));
@@ -335,31 +363,8 @@ async function loadRegistry(input = {}) {
335
363
 
336
364
  // src/registry/resolvePack.ts
337
365
  import path9 from "path";
366
+ import fg5 from "fast-glob";
338
367
  import fs8 from "fs-extra";
339
-
340
- // src/project/packageJson.ts
341
- import path8 from "path";
342
- import fs7 from "fs-extra";
343
- async function readPackageJson(root) {
344
- const packageJsonPath = path8.join(root, "package.json");
345
- if (!await fs7.pathExists(packageJsonPath)) {
346
- return null;
347
- }
348
- return fs7.readJson(packageJsonPath);
349
- }
350
- function hasPackage(packageJson, packageName) {
351
- if (!packageJson) {
352
- return false;
353
- }
354
- return Boolean(
355
- packageJson.dependencies?.[packageName] || packageJson.devDependencies?.[packageName] || packageJson.peerDependencies?.[packageName] || packageJson.optionalDependencies?.[packageName]
356
- );
357
- }
358
- function hasScript(packageJson, scriptName) {
359
- return Boolean(packageJson?.scripts?.[scriptName]);
360
- }
361
-
362
- // src/registry/resolvePack.ts
363
368
  function findPack(registry, packName) {
364
369
  return registry.find((pack) => pack.name === packName);
365
370
  }
@@ -373,29 +378,37 @@ function resolvePacks(packNames, registry) {
373
378
  return pack;
374
379
  });
375
380
  }
376
- function recommendPacks(analysis, registry) {
377
- const names = /* @__PURE__ */ new Set();
378
- names.add("env-secrets");
379
- if (analysis.framework === "next-app-router") {
380
- names.add("next-app-router");
381
- }
382
- if (analysis.database.prisma) {
383
- names.add("prisma-migrations");
384
- }
385
- if (analysis.styling.shadcn) {
386
- names.add("shadcn-ui");
381
+ async function hasDetectFile(root, filePattern) {
382
+ if (filePattern.includes("*")) {
383
+ const matches = await fg5(filePattern, {
384
+ cwd: root,
385
+ onlyFiles: false,
386
+ dot: true
387
+ });
388
+ return matches.length > 0;
387
389
  }
388
- if (analysis.testing.vitest || analysis.testing.jest || analysis.testing.playwright) {
389
- names.add("testing-workflow");
390
+ return fs8.pathExists(path9.join(root, filePattern));
391
+ }
392
+ function hasDetectHints(pack) {
393
+ return Boolean(pack.detect?.files?.length || pack.detect?.packages?.length);
394
+ }
395
+ async function recommendPacks(analysis, registry) {
396
+ const packageJson = await readPackageJson(analysis.root);
397
+ const recommended = [];
398
+ for (const pack of registry) {
399
+ if (pack.name === "env-secrets") {
400
+ recommended.push(pack);
401
+ continue;
402
+ }
403
+ if (hasDetectHints(pack) && await packMatchesProject(pack, analysis.root, packageJson)) {
404
+ recommended.push(pack);
405
+ }
390
406
  }
391
- return resolvePacks(
392
- [...names].filter((name) => findPack(registry, name)),
393
- registry
394
- );
407
+ return recommended;
395
408
  }
396
409
  async function packMatchesProject(pack, root, packageJson) {
397
410
  const fileChecks = await Promise.all(
398
- pack.detect?.files?.map((filePattern) => fs8.pathExists(path9.join(root, filePattern))) ?? []
411
+ pack.detect?.files?.map((filePattern) => hasDetectFile(root, filePattern)) ?? []
399
412
  );
400
413
  const packageChecks = pack.detect?.packages?.map((packageName) => hasPackage(packageJson, packageName)) ?? [];
401
414
  const checks = [...fileChecks, ...packageChecks];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contextforge/core",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",