@apso/cli 0.43.3 → 0.43.4

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.
@@ -66,6 +66,15 @@ class Generate extends base_command_1.default {
66
66
  console.log("[apso] No entities found in .apsorc — nothing to generate. Check that the file exists and defines an `entities` array.");
67
67
  }
68
68
  let generatedFileCount = 0;
69
+ // Every path this run writes. After generation, anything else under
70
+ // autogen/ that carries the Apso header belongs to an entity that was
71
+ // removed or renamed in .apsorc and gets deleted (apsoai/cli#157).
72
+ const generatedPaths = new Set();
73
+ const writeGenerated = (filePath, content) => {
74
+ const fullPath = path.join(autogenPath, filePath);
75
+ generatedPaths.add(fullPath);
76
+ return (0, file_system_1.createFile)(fullPath, content);
77
+ };
69
78
  const generatorConfig = {
70
79
  language,
71
80
  rootFolder,
@@ -87,17 +96,15 @@ class Generate extends base_command_1.default {
87
96
  // Generate enums
88
97
  const enumFiles = await generator.generateEnums(entities, lowerCaseApiType);
89
98
  for (const file of enumFiles) {
90
- const fullPath = path.join(autogenPath, file.path);
91
99
  // eslint-disable-next-line no-await-in-loop
92
- await (0, file_system_1.createFile)(fullPath, file.content);
100
+ await writeGenerated(file.path, file.content);
93
101
  }
94
102
  generatedFileCount += enumFiles.length;
95
103
  // Generate shared query utilities
96
104
  const queryUtilFiles = await generator.generateQueryUtils(entities, lowerCaseApiType);
97
105
  for (const file of queryUtilFiles) {
98
- const fullPath = path.join(autogenPath, file.path);
99
106
  // eslint-disable-next-line no-await-in-loop
100
- await (0, file_system_1.createFile)(fullPath, file.content);
107
+ await writeGenerated(file.path, file.content);
101
108
  }
102
109
  generatedFileCount += queryUtilFiles.length;
103
110
  // Generate per-entity files
@@ -173,9 +180,8 @@ class Generate extends base_command_1.default {
173
180
  // eslint-disable-next-line no-await-in-loop
174
181
  for (const files of allFiles) {
175
182
  for (const file of files) {
176
- const fullPath = path.join(autogenPath, file.path);
177
183
  // eslint-disable-next-line no-await-in-loop
178
- await (0, file_system_1.createFile)(fullPath, file.content);
184
+ await writeGenerated(file.path, file.content);
179
185
  generatedFileCount += 1;
180
186
  }
181
187
  }
@@ -185,26 +191,30 @@ class Generate extends base_command_1.default {
185
191
  // Generate guards
186
192
  const guardFiles = await generator.generateGuards(entities, auth);
187
193
  for (const file of guardFiles) {
188
- const fullPath = path.join(autogenPath, file.path);
189
194
  // eslint-disable-next-line no-await-in-loop
190
- await (0, file_system_1.createFile)(fullPath, file.content);
195
+ await writeGenerated(file.path, file.content);
191
196
  }
192
197
  generatedFileCount += guardFiles.length;
193
198
  // Generate domain-event spine (only when at least one entity opts in)
194
199
  const domainEventFiles = await generator.generateDomainEvents(entities, lowerCaseApiType, { emitEvents });
195
200
  for (const file of domainEventFiles) {
196
- const fullPath = path.join(autogenPath, file.path);
197
201
  // eslint-disable-next-line no-await-in-loop
198
- await (0, file_system_1.createFile)(fullPath, file.content);
202
+ await writeGenerated(file.path, file.content);
199
203
  }
200
204
  // Generate index module
201
205
  const indexFiles = await generator.generateIndexModule(entities, lowerCaseApiType, { emitEvents, http });
202
206
  for (const file of indexFiles) {
203
- const fullPath = path.join(autogenPath, file.path);
204
207
  // eslint-disable-next-line no-await-in-loop
205
- await (0, file_system_1.createFile)(fullPath, file.content);
208
+ await writeGenerated(file.path, file.content);
206
209
  }
207
210
  generatedFileCount += indexFiles.length;
211
+ // Skip cleanup when nothing was generated: an empty or missing .apsorc
212
+ // would otherwise wipe every generated file.
213
+ if (entities.length > 0) {
214
+ for (const removed of (0, file_system_1.removeStaleGeneratedFiles)(autogenPath, generatedPaths)) {
215
+ console.log(`[apso] Removed stale ${path.relative(process.cwd(), removed)}`);
216
+ }
217
+ }
208
218
  // Format generated files (TypeScript only) with Biome — bundled with the
209
219
  // CLI and invoked directly, so it's a fast native pass (~ms) with no npx
210
220
  // download and no dependence on the project having a formatter installed.
@@ -1,4 +1,12 @@
1
1
  export declare const createFile: (file: string, content: string) => Promise<void>;
2
+ /**
3
+ * Delete files under `dir` that carry the Apso header but are not in `keep`,
4
+ * i.e. files left over from entities removed or renamed in .apsorc. Files
5
+ * without the header (template seeds like .gitkeep, hand-written code) are
6
+ * never touched. Directories emptied this way are removed too. Returns the
7
+ * deleted paths.
8
+ */
9
+ export declare const removeStaleGeneratedFiles: (dir: string, keep: Set<string>) => string[];
2
10
  export declare const createDirectoryContents: (templatePath: string, newProjectPath: string, apiType: string) => void;
3
11
  export declare function withGeneratedMeta<T extends object>(data: T): T & {
4
12
  generatedAt: string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.withGeneratedMeta = exports.createDirectoryContents = exports.createFile = void 0;
3
+ exports.withGeneratedMeta = exports.createDirectoryContents = exports.removeStaleGeneratedFiles = exports.createFile = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const path = tslib_1.__importStar(require("path"));
6
6
  const fs = tslib_1.__importStar(require("fs"));
@@ -15,6 +15,36 @@ const createFile = async (file, content) => {
15
15
  await fs.promises.writeFile(file, content);
16
16
  };
17
17
  exports.createFile = createFile;
18
+ // Every file the CLI generates starts with this line (see templates/header.eta).
19
+ const APSO_HEADER = "Generated with Apso";
20
+ /**
21
+ * Delete files under `dir` that carry the Apso header but are not in `keep`,
22
+ * i.e. files left over from entities removed or renamed in .apsorc. Files
23
+ * without the header (template seeds like .gitkeep, hand-written code) are
24
+ * never touched. Directories emptied this way are removed too. Returns the
25
+ * deleted paths.
26
+ */
27
+ const removeStaleGeneratedFiles = (dir, keep) => {
28
+ if (!fs.existsSync(dir))
29
+ return [];
30
+ const removed = [];
31
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
32
+ const fullPath = path.join(dir, entry.name);
33
+ if (entry.isDirectory()) {
34
+ removed.push(...(0, exports.removeStaleGeneratedFiles)(fullPath, keep));
35
+ if (fs.readdirSync(fullPath).length === 0)
36
+ fs.rmdirSync(fullPath);
37
+ }
38
+ else if (entry.isFile() &&
39
+ !keep.has(fullPath) &&
40
+ fs.readFileSync(fullPath, "utf8").includes(APSO_HEADER)) {
41
+ fs.unlinkSync(fullPath);
42
+ removed.push(fullPath);
43
+ }
44
+ }
45
+ return removed;
46
+ };
47
+ exports.removeStaleGeneratedFiles = removeStaleGeneratedFiles;
18
48
  const createDirectoryContents = (templatePath, newProjectPath, apiType) => {
19
49
  const filesToCreate = fs.readdirSync(templatePath);
20
50
  const CURR_DIR = process.cwd();
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@apso/cli",
3
- "version": "0.43.3",
3
+ "version": "0.43.4",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@apso/cli",
9
- "version": "0.43.3",
9
+ "version": "0.43.4",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@apso/schema-tools": "^0.2.0",
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.43.3",
2
+ "version": "0.43.4",
3
3
  "commands": {
4
4
  "config": {
5
5
  "id": "config",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apso/cli",
3
- "version": "0.43.3",
3
+ "version": "0.43.4",
4
4
  "mcpName": "io.github.apsoai/apso",
5
5
  "description": "Apso CLI",
6
6
  "author": "Apso by Mavric - @mavric",