@nshiab/setup-data-project 1.0.39 → 2.0.1

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.
Files changed (60) hide show
  1. package/README.md +12 -5
  2. package/esm/helpers/createFolderStructure.d.ts.map +1 -1
  3. package/esm/helpers/createFolderStructure.js +4 -5
  4. package/esm/helpers/ensureAgents.d.ts +2 -1
  5. package/esm/helpers/ensureAgents.d.ts.map +1 -1
  6. package/esm/helpers/ensureAgents.js +40 -4
  7. package/esm/helpers/ensureManagedSection.d.ts.map +1 -1
  8. package/esm/helpers/ensureManagedSection.js +5 -2
  9. package/esm/helpers/ensureReadme.d.ts.map +1 -1
  10. package/esm/helpers/ensureReadme.js +7 -3
  11. package/esm/helpers/fetchPackageDocs.d.ts +8 -0
  12. package/esm/helpers/fetchPackageDocs.d.ts.map +1 -0
  13. package/esm/helpers/fetchPackageDocs.js +75 -0
  14. package/esm/helpers/getInstalledPackageVersions.d.ts +2 -0
  15. package/esm/helpers/getInstalledPackageVersions.d.ts.map +1 -0
  16. package/esm/helpers/getInstalledPackageVersions.js +91 -0
  17. package/esm/helpers/getProjectTasks.js +1 -1
  18. package/esm/helpers/installPackages.d.ts +7 -0
  19. package/esm/helpers/installPackages.d.ts.map +1 -0
  20. package/esm/helpers/installPackages.js +59 -0
  21. package/esm/helpers/projectManifest.d.ts +1 -0
  22. package/esm/helpers/projectManifest.d.ts.map +1 -1
  23. package/esm/helpers/projectManifest.js +10 -3
  24. package/esm/helpers/syncPackageDocs.d.ts +2 -1
  25. package/esm/helpers/syncPackageDocs.d.ts.map +1 -1
  26. package/esm/helpers/syncPackageDocs.js +31 -12
  27. package/esm/main.js +10 -5
  28. package/package.json +2 -2
  29. package/script/helpers/createFolderStructure.d.ts.map +1 -1
  30. package/script/helpers/createFolderStructure.js +4 -5
  31. package/script/helpers/ensureAgents.d.ts +2 -1
  32. package/script/helpers/ensureAgents.d.ts.map +1 -1
  33. package/script/helpers/ensureAgents.js +40 -4
  34. package/script/helpers/ensureManagedSection.d.ts.map +1 -1
  35. package/script/helpers/ensureManagedSection.js +5 -2
  36. package/script/helpers/ensureReadme.d.ts.map +1 -1
  37. package/script/helpers/ensureReadme.js +7 -3
  38. package/script/helpers/fetchPackageDocs.d.ts +8 -0
  39. package/script/helpers/fetchPackageDocs.d.ts.map +1 -0
  40. package/script/helpers/fetchPackageDocs.js +78 -0
  41. package/script/helpers/getInstalledPackageVersions.d.ts +2 -0
  42. package/script/helpers/getInstalledPackageVersions.d.ts.map +1 -0
  43. package/script/helpers/getInstalledPackageVersions.js +94 -0
  44. package/script/helpers/getProjectTasks.js +1 -1
  45. package/script/helpers/installPackages.d.ts +7 -0
  46. package/script/helpers/installPackages.d.ts.map +1 -0
  47. package/script/helpers/installPackages.js +96 -0
  48. package/script/helpers/projectManifest.d.ts +1 -0
  49. package/script/helpers/projectManifest.d.ts.map +1 -1
  50. package/script/helpers/projectManifest.js +10 -3
  51. package/script/helpers/syncPackageDocs.d.ts +2 -1
  52. package/script/helpers/syncPackageDocs.d.ts.map +1 -1
  53. package/script/helpers/syncPackageDocs.js +30 -11
  54. package/script/main.js +10 -5
  55. package/esm/helpers/installPackagesAndFetchDocs.d.ts +0 -10
  56. package/esm/helpers/installPackagesAndFetchDocs.d.ts.map +0 -1
  57. package/esm/helpers/installPackagesAndFetchDocs.js +0 -100
  58. package/script/helpers/installPackagesAndFetchDocs.d.ts +0 -10
  59. package/script/helpers/installPackagesAndFetchDocs.d.ts.map +0 -1
  60. package/script/helpers/installPackagesAndFetchDocs.js +0 -138
package/README.md CHANGED
@@ -5,7 +5,7 @@ configurations, and documentation.
5
5
 
6
6
  ```sh
7
7
  # Deno
8
- deno run -A jsr:@nshiab/setup-data-project
8
+ deno run --min-dep-age=0 -A jsr:@nshiab/setup-data-project
9
9
 
10
10
  # Node
11
11
  npx @nshiab/setup-data-project
@@ -14,12 +14,19 @@ npx @nshiab/setup-data-project
14
14
  bunx @nshiab/setup-data-project
15
15
  ```
16
16
 
17
+ The Deno dependency-age bypass is temporary while the 2.0 dependencies are newly
18
+ published. It is tracked for removal in
19
+ [issue #15](https://github.com/nshiab/setup-data-project/issues/15).
20
+
17
21
  - Creates a standardized folder structure.
18
22
  - Ensures necessary files like `.env`, `.gitignore`, and `README.md` exist.
19
- - Installs
20
- [simple-data-analysis](https://github.com/nshiab/simple-data-analysis/) and
21
- [journalism](https://github.com/nshiab/journalism) libraries and fetches their
22
- documentation for LLM use.
23
+ - Lets you choose between
24
+ [simple-data-analysis-core](https://github.com/nshiab/simple-data-analysis-core),
25
+ the full
26
+ [simple-data-analysis](https://github.com/nshiab/simple-data-analysis/)
27
+ package, and [journalism](https://github.com/nshiab/journalism) libraries,
28
+ then fetches their README files and complete API documentation from the
29
+ matching version tags on GitHub for LLM use.
23
30
  - Updates project configuration (e.g., `deno.json` or `package.json`) with
24
31
  relevant tasks.
25
32
 
@@ -1 +1 @@
1
- {"version":3,"file":"createFolderStructure.d.ts","sourceRoot":"","sources":["../../src/helpers/createFolderStructure.ts"],"names":[],"mappings":"AAIA,wBAAgB,qBAAqB,CAAC,gBAAgB,EAAE,MAAM,EAAE,UA8C/D"}
1
+ {"version":3,"file":"createFolderStructure.d.ts","sourceRoot":"","sources":["../../src/helpers/createFolderStructure.ts"],"names":[],"mappings":"AAIA,wBAAgB,qBAAqB,CAAC,gBAAgB,EAAE,MAAM,EAAE,UA6C/D"}
@@ -25,12 +25,11 @@ export function createFolderStructure(selectedPackages) {
25
25
  const sdb = new SimpleDB();
26
26
  const table = sdb.newTable();
27
27
 
28
- // Do your magic here!
29
- // await table.loadData("./sda/data/...");
28
+ await table
29
+ .loadArray([{ message: "Hello simple-data-analysis!" }])
30
+ .log();
30
31
 
31
- await table.logTable();
32
-
33
- await sdb.done();
32
+ await sdb.close();
34
33
  `;
35
34
  }
36
35
  if (lstatSync(mainTsPath, { throwIfNoEntry: false }) === undefined) {
@@ -1,2 +1,3 @@
1
- export declare function ensureAgents(docsMapping: Record<string, string>, runtime: string, installedPackages?: string[]): void;
1
+ import type { PackageDocs } from "./fetchPackageDocs.js";
2
+ export declare function ensureAgents(docsMapping: Record<string, PackageDocs>, runtime: string, installedPackages?: string[]): void;
2
3
  //# sourceMappingURL=ensureAgents.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ensureAgents.d.ts","sourceRoot":"","sources":["../../src/helpers/ensureAgents.ts"],"names":[],"mappings":"AAOA,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACnC,OAAO,EAAE,MAAM,EACf,iBAAiB,GAAE,MAAM,EAA6B,QAiJvD"}
1
+ {"version":3,"file":"ensureAgents.d.ts","sourceRoot":"","sources":["../../src/helpers/ensureAgents.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAOzD,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACxC,OAAO,EAAE,MAAM,EACf,iBAAiB,GAAE,MAAM,EAA6B,QAqLvD"}
@@ -5,11 +5,15 @@ export function ensureAgents(docsMapping, runtime, installedPackages = Object.ke
5
5
  let journalismFunctions = "";
6
6
  let sdaClassesAndMethods = "";
7
7
  let sdaDocsPath = "";
8
+ let sdaReadmePath = "";
8
9
  let sdaLibraryName = "";
9
10
  const journalismFunctionsByPackage = new Map();
10
- for (const [pkg, doc] of Object.entries(docsMapping)) {
11
+ for (const [pkg, docs] of Object.entries(docsMapping)) {
11
12
  const pkgConfig = SUPPORTED_PACKAGES.find((p) => p.value === pkg);
12
13
  const pkgType = pkgConfig?.type || "other";
14
+ const doc = docs.llm;
15
+ if (doc === undefined)
16
+ continue;
13
17
  if (pkgType === "journalism") {
14
18
  const repoName = pkg.split("/")[1];
15
19
  const functions = doc
@@ -27,7 +31,10 @@ export function ensureAgents(docsMapping, runtime, installedPackages = Object.ke
27
31
  }
28
32
  else if (pkgType === "sda") {
29
33
  const repoName = pkg.split("/")[1];
30
- sdaDocsPath = `./docs/${repoName}.md`;
34
+ sdaDocsPath = `./docs/${repoName}/llm.md`;
35
+ sdaReadmePath = docs.readme === undefined
36
+ ? ""
37
+ : `./docs/${repoName}/README.md`;
31
38
  sdaLibraryName = pkgConfig?.label || repoName;
32
39
  sdaClassesAndMethods = doc
33
40
  .split("\n")
@@ -89,6 +96,30 @@ export function ensureAgents(docsMapping, runtime, installedPackages = Object.ke
89
96
  libraryGuidance = `
90
97
  Always prioritize the installed ${libraryNames} ${libraryNoun} when relevant.
91
98
  `;
99
+ const documentationGuidance = installedPackageConfigs.flatMap((pkg) => {
100
+ const docs = docsMapping[pkg.value];
101
+ if (!docs)
102
+ return [];
103
+ const repoName = pkg.value.split("/")[1];
104
+ const availableDocs = [
105
+ docs.readme === undefined
106
+ ? ""
107
+ : `overview and practical examples at \`./docs/${repoName}/README.md\``,
108
+ docs.llm === undefined
109
+ ? ""
110
+ : `complete API documentation at \`./docs/${repoName}/llm.md\``,
111
+ ].filter(Boolean);
112
+ return availableDocs.length === 0
113
+ ? []
114
+ : [`- \`${pkg.value}\`: ${availableDocs.join("; ")}`];
115
+ });
116
+ if (documentationGuidance.length > 0) {
117
+ libraryGuidance += `
118
+ The following documentation was fetched for the exact installed library versions. Consult it when relevant:
119
+
120
+ ${documentationGuidance.join("\n")}
121
+ `;
122
+ }
92
123
  if (importExamples.length > 0) {
93
124
  libraryGuidance += `APIs can be imported with named imports like this:
94
125
  \`\`\`typescript
@@ -117,12 +148,17 @@ If you need to output data to a file, always put the file in the "sda/output" fo
117
148
  ${libraryGuidance}`;
118
149
  if (journalismFunctions !== "") {
119
150
  content += `
120
- Here are the functions available in the "journalism" libraries. If one of the function might be relevant, read the complete documentation in the "./docs/" folder to properly use it.
151
+ Here are the functions available in the "journalism" libraries. If one of the functions might be relevant, search its complete API documentation in the corresponding \`./docs/<library-name>/llm.md\` file to properly use it.
121
152
  ${journalismFunctions}`;
122
153
  }
123
154
  if (sdaClassesAndMethods !== "") {
155
+ const readmeGuidance = sdaReadmePath === ""
156
+ ? ""
157
+ : ` An overview and practical examples are available at "${sdaReadmePath}".`;
124
158
  content += `
125
- Here are the classes and their methods available in the "${sdaLibraryName}" library. If one of the classes or methods might be relevant, read the complete documentation at "${sdaDocsPath}" to properly use it. Remember that almost all methods are asynchronous, so you need to use \`await\` when calling them.
159
+ Here are the classes and their methods available in the "${sdaLibraryName}" library.${readmeGuidance} If one of the classes or methods might be relevant, search the complete API documentation at "${sdaDocsPath}" to properly use it.
160
+
161
+ Most data-loading and transformation methods are synchronous, chainable builders. Await the final asynchronous observer or export method, such as \`log()\`, \`getData()\`, or \`writeData()\`, to execute the queued operations. If a chain ends without an observer, call \`run()\`. Always call \`await sdb.close()\` when the database is no longer needed. Methods that return an answer or export to an external service can be asynchronous, so verify their documentation before calling them.
126
162
  ${sdaClassesAndMethods}`;
127
163
  }
128
164
  const status = ensureManagedSection({
@@ -1 +1 @@
1
- {"version":3,"file":"ensureManagedSection.d.ts","sourceRoot":"","sources":["../../src/helpers/ensureManagedSection.ts"],"names":[],"mappings":"AAGA,UAAU,qBAAqB;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;CAC5C;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,qBAAqB,GAC7B,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,iBAAiB,CAqCzD"}
1
+ {"version":3,"file":"ensureManagedSection.d.ts","sourceRoot":"","sources":["../../src/helpers/ensureManagedSection.ts"],"names":[],"mappings":"AAGA,UAAU,qBAAqB;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;CAC5C;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,qBAAqB,GAC7B,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,iBAAiB,CAwCzD"}
@@ -8,7 +8,7 @@ export function ensureManagedSection(options) {
8
8
  ? `${startMarker}\n${endMarker}`
9
9
  : `${startMarker}\n${trimmedContent}\n${endMarker}`;
10
10
  if (!existsSync(options.path)) {
11
- writeFileSync(options.path, options.createContent(section));
11
+ writeFileSync(options.path, ensureFinalNewline(options.createContent(section)));
12
12
  return "created";
13
13
  }
14
14
  const currentContent = readFileSync(options.path, "utf-8");
@@ -25,6 +25,9 @@ export function ensureManagedSection(options) {
25
25
  if (nextContent === currentContent) {
26
26
  return "unchanged";
27
27
  }
28
- writeFileSync(options.path, nextContent);
28
+ writeFileSync(options.path, ensureFinalNewline(nextContent));
29
29
  return "updated";
30
30
  }
31
+ function ensureFinalNewline(content) {
32
+ return content.endsWith("\n") ? content : content + "\n";
33
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"ensureReadme.d.ts","sourceRoot":"","sources":["../../src/helpers/ensureReadme.ts"],"names":[],"mappings":"AAIA,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,iBAAiB,GAAE,MAAM,EAAO,QA4DjC"}
1
+ {"version":3,"file":"ensureReadme.d.ts","sourceRoot":"","sources":["../../src/helpers/ensureReadme.ts"],"names":[],"mappings":"AAIA,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,iBAAiB,GAAE,MAAM,EAAO,QAsEjC"}
@@ -10,17 +10,21 @@ export function ensureReadme(runtime, installedPackages = []) {
10
10
  runCommand = "bun run";
11
11
  }
12
12
  const installedPackageConfigs = getInstalledPackageConfigs(installedPackages);
13
- const hasSda = installedPackageConfigs.some((pkg) => pkg.type === "sda");
13
+ const hasSdaCore = installedPackageConfigs.some((pkg) => pkg.value === "@nshiab/simple-data-analysis-core");
14
+ const hasFullSda = installedPackageConfigs.some((pkg) => pkg.value === "@nshiab/simple-data-analysis");
14
15
  const hasJournalism = installedPackageConfigs.some((pkg) => pkg.type === "journalism");
15
16
  const libraryLinks = [
16
- hasSda
17
+ hasSdaCore
18
+ ? "[simple-data-analysis-core](https://github.com/nshiab/simple-data-analysis-core)"
19
+ : "",
20
+ hasFullSda
17
21
  ? "[simple-data-analysis](https://github.com/nshiab/simple-data-analysis)"
18
22
  : "",
19
23
  hasJournalism ? "[journalism](https://github.com/nshiab/journalism)" : "",
20
24
  ].filter(Boolean);
21
25
  const librarySection = libraryLinks.length === 0
22
26
  ? ""
23
- : `It has installed ${libraryLinks.join(" and ")} libraries, along with
27
+ : `It has installed ${libraryLinks.join(" and ")} ${libraryLinks.length === 1 ? "library" : "libraries"}, along with
24
28
  up-to-date documentation and AI agent instructions.`;
25
29
  const status = ensureManagedSection({
26
30
  path: "README.md",
@@ -0,0 +1,8 @@
1
+ export interface PackageDocs {
2
+ readme?: string;
3
+ llm?: string;
4
+ }
5
+ export declare function fetchPackageDocs(pkg: string, version: string, options?: {
6
+ silent?: boolean;
7
+ }): Promise<PackageDocs>;
8
+ //# sourceMappingURL=fetchPackageDocs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetchPackageDocs.d.ts","sourceRoot":"","sources":["../../src/helpers/fetchPackageDocs.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAiBD,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,OAAO,CAAC,WAAW,CAAC,CA4DtB"}
@@ -0,0 +1,75 @@
1
+ import { log, spinner } from "@clack/prompts";
2
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ const PACKAGE_DOCUMENTS = [
5
+ {
6
+ remoteName: "README.md",
7
+ localName: "README.md",
8
+ key: "readme",
9
+ label: "README",
10
+ },
11
+ {
12
+ remoteName: "llm.md",
13
+ localName: "llm.md",
14
+ key: "llm",
15
+ label: "API documentation",
16
+ },
17
+ ];
18
+ export async function fetchPackageDocs(pkg, version, options = {}) {
19
+ const repoName = pkg.split("/")[1];
20
+ const docsDirectory = join("docs", repoName);
21
+ mkdirSync(docsDirectory, { recursive: true });
22
+ const sFetch = spinner();
23
+ if (!options.silent) {
24
+ sFetch.start(`Fetching documentation for ${pkg}@${version}...`);
25
+ }
26
+ const fetchedDocuments = await Promise.all(PACKAGE_DOCUMENTS.map(async (document) => {
27
+ const localPath = join(docsDirectory, document.localName);
28
+ try {
29
+ const url = "https://raw.githubusercontent.com/nshiab/" + repoName +
30
+ `/refs/tags/v${version}/${document.remoteName}`;
31
+ const response = await fetch(url);
32
+ if (!response.ok) {
33
+ warnAboutFailedFetch(pkg, version, document.label, localPath);
34
+ return { key: document.key, content: undefined };
35
+ }
36
+ const content = await response.text();
37
+ writeFileSync(localPath, content);
38
+ return { key: document.key, content };
39
+ }
40
+ catch (error) {
41
+ warnAboutFailedFetch(pkg, version, document.label, localPath);
42
+ console.error(error);
43
+ return { key: document.key, content: undefined };
44
+ }
45
+ }));
46
+ const docs = {};
47
+ for (const document of fetchedDocuments) {
48
+ if (document.content !== undefined) {
49
+ docs[document.key] = document.content;
50
+ }
51
+ }
52
+ const savedCount = fetchedDocuments.filter((document) => document.content !== undefined)
53
+ .length;
54
+ if (!options.silent) {
55
+ if (savedCount === PACKAGE_DOCUMENTS.length) {
56
+ sFetch.stop(`✅ Documentation for ${pkg}@${version} saved!`);
57
+ }
58
+ else if (savedCount > 0) {
59
+ sFetch.stop(`⚠️ Some documentation for ${pkg}@${version} could not be fetched.`);
60
+ }
61
+ else {
62
+ sFetch.stop(`❌ Failed to fetch documentation for ${pkg}@${version}.`);
63
+ }
64
+ }
65
+ return docs;
66
+ }
67
+ function warnAboutFailedFetch(pkg, version, label, localPath) {
68
+ const existingFileGuidance = existsSync(localPath)
69
+ ? ` The existing file at ${localPath} was preserved, but it may not ` +
70
+ "match the installed version. Double-check it before relying on it, or " +
71
+ "remove it."
72
+ : "";
73
+ log.warn(`Could not fetch ${label} for ${pkg}@${version} from its GitHub tag.` +
74
+ existingFileGuidance);
75
+ }
@@ -0,0 +1,2 @@
1
+ export declare function getInstalledPackageVersions(packageSpecifiers: Record<string, string>): Record<string, string>;
2
+ //# sourceMappingURL=getInstalledPackageVersions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getInstalledPackageVersions.d.ts","sourceRoot":"","sources":["../../src/helpers/getInstalledPackageVersions.ts"],"names":[],"mappings":"AAKA,wBAAgB,2BAA2B,CACzC,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACxC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAuBxB"}
@@ -0,0 +1,91 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ export function getInstalledPackageVersions(packageSpecifiers) {
4
+ const denoLock = readJsonFile("deno.lock");
5
+ const packageLock = readJsonFile("package-lock.json");
6
+ const bunLock = existsSync("bun.lock")
7
+ ? readFileSync("bun.lock", "utf-8")
8
+ : undefined;
9
+ const versions = {};
10
+ for (const [pkg, specifier] of Object.entries(packageSpecifiers)) {
11
+ const version = specifier.startsWith("jsr:")
12
+ ? getDenoLockVersion(pkg, specifier, denoLock) ??
13
+ getExactVersion(specifier)
14
+ : getNodeModulesVersion(pkg) ??
15
+ getPackageLockVersion(pkg, packageLock) ??
16
+ getBunLockVersion(pkg, bunLock) ??
17
+ getExactVersion(specifier);
18
+ if (version !== undefined) {
19
+ versions[pkg] = version;
20
+ }
21
+ }
22
+ return versions;
23
+ }
24
+ function getNodeModulesVersion(pkg) {
25
+ const packageJson = readJsonFile(join("node_modules", ...pkg.split("/"), "package.json"));
26
+ return typeof packageJson?.version === "string"
27
+ ? getExactVersion(packageJson.version)
28
+ : undefined;
29
+ }
30
+ function getDenoLockVersion(pkg, specifier, lock) {
31
+ if (!lock || !specifier.startsWith("jsr:"))
32
+ return undefined;
33
+ const specifiers = asObject(lock.specifiers);
34
+ if (!specifiers)
35
+ return undefined;
36
+ const directVersion = specifiers[specifier];
37
+ if (typeof directVersion === "string")
38
+ return getExactVersion(directVersion);
39
+ const prefix = `jsr:${pkg}@`;
40
+ const matchingVersions = Object.entries(specifiers)
41
+ .filter(([key, value]) => key.startsWith(prefix) && typeof value === "string")
42
+ .flatMap(([, value]) => {
43
+ const version = getExactVersion(value);
44
+ return version === undefined ? [] : [version];
45
+ });
46
+ const uniqueVersions = [...new Set(matchingVersions)];
47
+ return uniqueVersions.length === 1 ? uniqueVersions[0] : undefined;
48
+ }
49
+ function getPackageLockVersion(pkg, lock) {
50
+ if (!lock)
51
+ return undefined;
52
+ const packages = asObject(lock.packages);
53
+ const packageEntry = packages
54
+ ? asObject(packages[`node_modules/${pkg}`])
55
+ : undefined;
56
+ if (typeof packageEntry?.version === "string") {
57
+ return getExactVersion(packageEntry.version);
58
+ }
59
+ const dependencies = asObject(lock.dependencies);
60
+ const dependencyEntry = dependencies
61
+ ? asObject(dependencies[pkg])
62
+ : undefined;
63
+ return typeof dependencyEntry?.version === "string"
64
+ ? getExactVersion(dependencyEntry.version)
65
+ : undefined;
66
+ }
67
+ function getBunLockVersion(pkg, lock) {
68
+ if (!lock)
69
+ return undefined;
70
+ const escapedPackage = pkg.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
71
+ const match = lock.match(new RegExp(`"${escapedPackage}"\\s*:\\s*\\[\\s*"${escapedPackage}@(\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?)"`));
72
+ return match?.[1];
73
+ }
74
+ function getExactVersion(specifier) {
75
+ return specifier.match(/(?:^|@)(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)$/)?.[1];
76
+ }
77
+ function readJsonFile(path) {
78
+ if (!existsSync(path))
79
+ return undefined;
80
+ try {
81
+ return asObject(JSON.parse(readFileSync(path, "utf-8")));
82
+ }
83
+ catch {
84
+ return undefined;
85
+ }
86
+ }
87
+ function asObject(value) {
88
+ return value !== null && typeof value === "object" && !Array.isArray(value)
89
+ ? value
90
+ : undefined;
91
+ }
@@ -8,7 +8,7 @@ export function getProjectTasks() {
8
8
  ? "bun run --watch sda/main.ts"
9
9
  : "node --env-file-if-exists=.env --watch --experimental-strip-types sda/main.ts",
10
10
  "all-tests": runtime === "deno"
11
- ? "deno fmt --check && deno lint && deno check sda/main.ts && deno test -A"
11
+ ? "deno fmt --check sda && deno lint && deno check sda/main.ts && deno test -A --permit-no-files"
12
12
  : runtime === "bun"
13
13
  ? "bun test --pass-with-no-tests"
14
14
  : "node --env-file-if-exists=.env --experimental-strip-types --test",
@@ -0,0 +1,7 @@
1
+ export declare const commandRunner: {
2
+ exec: any;
3
+ };
4
+ export declare function installPackages(selectedPackages: string[], options?: {
5
+ silent?: boolean;
6
+ }): Promise<void>;
7
+ //# sourceMappingURL=installPackages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installPackages.d.ts","sourceRoot":"","sources":["../../src/helpers/installPackages.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,aAAa;;CAEzB,CAAC;AASF,wBAAsB,eAAe,CACnC,gBAAgB,EAAE,MAAM,EAAE,EAC1B,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,OAAO,CAAC,IAAI,CAAC,CAmDf"}
@@ -0,0 +1,59 @@
1
+ import { spinner } from "@clack/prompts";
2
+ import * as childProcess from "node:child_process";
3
+ import { getRuntime } from "./getRuntime.js";
4
+ export const commandRunner = {
5
+ exec: childProcess.exec,
6
+ };
7
+ // Temporary: remove this bypass after the 2.0 release window.
8
+ // https://github.com/nshiab/setup-data-project/issues/15
9
+ const PACKAGES_WITH_FRESH_DENO_RELEASES = new Set([
10
+ "@nshiab/simple-data-analysis-core",
11
+ "@nshiab/simple-data-analysis",
12
+ ]);
13
+ export async function installPackages(selectedPackages, options = {}) {
14
+ const runtime = getRuntime();
15
+ for (const selectedPackage of selectedPackages) {
16
+ const packagesToInstall = [selectedPackage];
17
+ if (selectedPackage === "@nshiab/simple-data-analysis" ||
18
+ selectedPackage === "@nshiab/journalism-dataviz") {
19
+ packagesToInstall.push("@observablehq/plot");
20
+ }
21
+ for (const pkg of packagesToInstall) {
22
+ const s = spinner();
23
+ const isObservablePlot = pkg === "@observablehq/plot";
24
+ const minimumDependencyAgeOption = PACKAGES_WITH_FRESH_DENO_RELEASES.has(pkg) ? " --min-dep-age=0" : "";
25
+ const installCmd = runtime === "deno"
26
+ ? (isObservablePlot
27
+ ? "deno add npm:" + pkg
28
+ : "deno add" + minimumDependencyAgeOption + " jsr:" +
29
+ pkg)
30
+ : runtime === "bun"
31
+ ? "bun add " + pkg
32
+ : "npm install " + pkg;
33
+ if (!options.silent) {
34
+ s.start("Installing " + pkg + " via " + runtime + "...");
35
+ }
36
+ try {
37
+ await new Promise((resolve, reject) => {
38
+ commandRunner.exec(installCmd, (error) => {
39
+ if (error) {
40
+ reject(error);
41
+ }
42
+ else {
43
+ resolve();
44
+ }
45
+ });
46
+ });
47
+ if (!options.silent) {
48
+ s.stop("✅ " + pkg + " installed!");
49
+ }
50
+ }
51
+ catch (error) {
52
+ if (!options.silent) {
53
+ s.stop("❌ Failed to install " + pkg + ".");
54
+ }
55
+ throw error;
56
+ }
57
+ }
58
+ }
59
+ }
@@ -5,6 +5,7 @@ export declare class ProjectManifest {
5
5
  static open(runtime: Runtime): ProjectManifest;
6
6
  reload(): void;
7
7
  getInstalledPackages(): string[];
8
+ getInstalledPackageSpecifiers(): Record<string, string>;
8
9
  updateTasks(tasks: Record<string, string>): void;
9
10
  }
10
11
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"projectManifest.d.ts","sourceRoot":"","sources":["../../src/helpers/projectManifest.ts"],"names":[],"mappings":"AAEA,KAAK,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAGvC,qBAAa,eAAe;;IAM1B,OAAO;IAYP,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,eAAe;IA6B9C,MAAM,IAAI,IAAI;IAId,oBAAoB,IAAI,MAAM,EAAE;IAgBhC,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;CAUjD"}
1
+ {"version":3,"file":"projectManifest.d.ts","sourceRoot":"","sources":["../../src/helpers/projectManifest.ts"],"names":[],"mappings":"AAEA,KAAK,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAGvC,qBAAa,eAAe;;IAM1B,OAAO;IAYP,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,eAAe;IA6B9C,MAAM,IAAI,IAAI;IAId,oBAAoB,IAAI,MAAM,EAAE;IAIhC,6BAA6B,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAoBvD,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;CAcjD"}
@@ -37,12 +37,19 @@ export class ProjectManifest {
37
37
  __classPrivateFieldSet(this, _ProjectManifest_config, readManifest(__classPrivateFieldGet(this, _ProjectManifest_path, "f")), "f");
38
38
  }
39
39
  getInstalledPackages() {
40
- const installed = [];
40
+ return Object.keys(this.getInstalledPackageSpecifiers());
41
+ }
42
+ getInstalledPackageSpecifiers() {
43
+ const installed = {};
41
44
  for (const key of __classPrivateFieldGet(this, _ProjectManifest_dependencyKeys, "f")) {
42
45
  const dependencies = __classPrivateFieldGet(this, _ProjectManifest_config, "f")[key];
43
46
  if (dependencies && typeof dependencies === "object" &&
44
47
  !Array.isArray(dependencies)) {
45
- installed.push(...Object.keys(dependencies));
48
+ for (const [name, specifier] of Object.entries(dependencies)) {
49
+ if (typeof specifier === "string") {
50
+ installed[name] = specifier;
51
+ }
52
+ }
46
53
  }
47
54
  }
48
55
  return installed;
@@ -54,7 +61,7 @@ export class ProjectManifest {
54
61
  ? currentTasks
55
62
  : {};
56
63
  __classPrivateFieldGet(this, _ProjectManifest_config, "f")[__classPrivateFieldGet(this, _ProjectManifest_taskKey, "f")] = { ...existingTasks, ...tasks };
57
- writeFileSync(__classPrivateFieldGet(this, _ProjectManifest_path, "f"), JSON.stringify(__classPrivateFieldGet(this, _ProjectManifest_config, "f"), null, 2), "utf-8");
64
+ writeFileSync(__classPrivateFieldGet(this, _ProjectManifest_path, "f"), JSON.stringify(__classPrivateFieldGet(this, _ProjectManifest_config, "f"), null, 2) + "\n", "utf-8");
58
65
  }
59
66
  }
60
67
  _ProjectManifest_path = new WeakMap(), _ProjectManifest_taskKey = new WeakMap(), _ProjectManifest_dependencyKeys = new WeakMap(), _ProjectManifest_config = new WeakMap();
@@ -1,2 +1,3 @@
1
- export declare function syncPackageDocs(installedPackages: string[], docsAlreadyRefreshed?: string[]): Promise<Record<string, string>>;
1
+ import { type PackageDocs } from "./fetchPackageDocs.js";
2
+ export declare function syncPackageDocs(installedPackages: string[], installedVersions: Record<string, string>): Promise<Record<string, PackageDocs>>;
2
3
  //# sourceMappingURL=syncPackageDocs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"syncPackageDocs.d.ts","sourceRoot":"","sources":["../../src/helpers/syncPackageDocs.ts"],"names":[],"mappings":"AAMA,wBAAsB,eAAe,CACnC,iBAAiB,EAAE,MAAM,EAAE,EAC3B,oBAAoB,GAAE,MAAM,EAAO,GAClC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA0BjC"}
1
+ {"version":3,"file":"syncPackageDocs.d.ts","sourceRoot":"","sources":["../../src/helpers/syncPackageDocs.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoB,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAG3E,wBAAsB,eAAe,CACnC,iBAAiB,EAAE,MAAM,EAAE,EAC3B,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACxC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CA+CtC"}
@@ -1,26 +1,45 @@
1
1
  import { log } from "@clack/prompts";
2
- import { existsSync, readFileSync, rmSync } from "node:fs";
2
+ import { existsSync, mkdirSync, renameSync, rmSync } from "node:fs";
3
3
  import { join } from "node:path";
4
- import { fetchPackageDocs } from "./installPackagesAndFetchDocs.js";
4
+ import { fetchPackageDocs } from "./fetchPackageDocs.js";
5
5
  import { SUPPORTED_PACKAGES } from "./packageRegistry.js";
6
- export async function syncPackageDocs(installedPackages, docsAlreadyRefreshed = []) {
6
+ export async function syncPackageDocs(installedPackages, installedVersions) {
7
7
  const installedSet = new Set(installedPackages);
8
- const refreshedSet = new Set(docsAlreadyRefreshed);
9
8
  const docsMapping = {};
10
9
  for (const pkg of SUPPORTED_PACKAGES) {
11
- const docPath = join("docs", pkg.value.split("/")[1] + ".md");
10
+ const repoName = pkg.value.split("/")[1];
11
+ const docsDirectory = join("docs", repoName);
12
+ const llmDocsPath = join(docsDirectory, "llm.md");
13
+ const legacyDocsPath = join("docs", repoName + ".md");
12
14
  if (!installedSet.has(pkg.value)) {
13
- if (existsSync(docPath)) {
14
- rmSync(docPath);
15
- log.info(`Removed stale documentation at ${docPath}.`);
15
+ for (const path of [docsDirectory, legacyDocsPath]) {
16
+ if (existsSync(path)) {
17
+ rmSync(path, { recursive: true });
18
+ log.info(`Removed stale documentation at ${path}.`);
19
+ }
16
20
  }
17
21
  continue;
18
22
  }
19
- if (!existsSync(docPath) && !refreshedSet.has(pkg.value)) {
20
- await fetchPackageDocs(pkg.value, { silent: true });
23
+ if (existsSync(legacyDocsPath)) {
24
+ if (!existsSync(llmDocsPath)) {
25
+ mkdirSync(docsDirectory, { recursive: true });
26
+ renameSync(legacyDocsPath, llmDocsPath);
27
+ log.info(`Moved documentation to ${llmDocsPath}.`);
28
+ }
29
+ else {
30
+ rmSync(legacyDocsPath);
31
+ }
32
+ }
33
+ const version = installedVersions[pkg.value];
34
+ if (version === undefined) {
35
+ log.warn(`Could not determine the exact installed version of ${pkg.value}. ` +
36
+ "Existing local documentation was preserved but will not be " +
37
+ "included in AGENTS.md.");
38
+ continue;
21
39
  }
22
- if (existsSync(docPath)) {
23
- docsMapping[pkg.value] = readFileSync(docPath, "utf-8");
40
+ const docs = await fetchPackageDocs(pkg.value, version, { silent: true });
41
+ if (docs.readme !== undefined || docs.llm !== undefined) {
42
+ docsMapping[pkg.value] = docs;
24
43
  }
25
44
  }
26
45
  return docsMapping;
package/esm/main.js CHANGED
@@ -5,7 +5,7 @@ import { getRuntime } from "./helpers/getRuntime.js";
5
5
  import { PACKAGE_OPTIONS } from "./helpers/packageOptions.js";
6
6
  import { ensureGitignore } from "./helpers/ensureGitignore.js";
7
7
  import { ensureReadme } from "./helpers/ensureReadme.js";
8
- import { installPackagesAndFetchDocs } from "./helpers/installPackagesAndFetchDocs.js";
8
+ import { installPackages } from "./helpers/installPackages.js";
9
9
  import { getProjectTasks } from "./helpers/getProjectTasks.js";
10
10
  import { createFolderStructure } from "./helpers/createFolderStructure.js";
11
11
  import { ensureEnvFile } from "./helpers/ensureEnvFile.js";
@@ -13,6 +13,7 @@ import { ensureAgents } from "./helpers/ensureAgents.js";
13
13
  import { getInstalledPackageConfigs } from "./helpers/packageRegistry.js";
14
14
  import { ProjectManifest } from "./helpers/projectManifest.js";
15
15
  import { syncPackageDocs } from "./helpers/syncPackageDocs.js";
16
+ import { getInstalledPackageVersions } from "./helpers/getInstalledPackageVersions.js";
16
17
  async function main() {
17
18
  const runtime = getRuntime();
18
19
  console.log();
@@ -59,10 +60,10 @@ async function main() {
59
60
  }
60
61
  }
61
62
  const sInstall = spinner();
62
- sInstall.start("Installing packages and fetching documentation...");
63
+ sInstall.start("Installing packages...");
63
64
  for (const pkg of packagesToInstall) {
64
65
  try {
65
- await installPackagesAndFetchDocs([pkg], { silent: true });
66
+ await installPackages([pkg], { silent: true });
66
67
  }
67
68
  catch (error) {
68
69
  sInstall.stop(`❌ Failed to install ${pkg}.`);
@@ -70,11 +71,15 @@ async function main() {
70
71
  process.exit(1);
71
72
  }
72
73
  }
73
- sInstall.stop("✅ Packages installed and documentation fetched.");
74
+ sInstall.stop("✅ Package installation complete.");
74
75
  projectManifest.reload();
75
76
  projectManifest.updateTasks(getProjectTasks());
76
77
  const finalInstalledPackages = getInstalledPackageConfigs(projectManifest.getInstalledPackages()).map((pkg) => pkg.value);
77
- const docsMapping = await syncPackageDocs(finalInstalledPackages, packagesToInstall);
78
+ const finalInstalledVersions = getInstalledPackageVersions(projectManifest.getInstalledPackageSpecifiers());
79
+ const sDocs = spinner();
80
+ sDocs.start("Fetching documentation for installed package versions...");
81
+ const docsMapping = await syncPackageDocs(finalInstalledPackages, finalInstalledVersions);
82
+ sDocs.stop("✅ Documentation synchronization complete.");
78
83
  createFolderStructure(finalInstalledPackages);
79
84
  ensureGitignore();
80
85
  ensureEnvFile();