@xano/cli 0.0.88 → 0.0.90

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.
@@ -16,6 +16,8 @@ export default class Push extends BaseCommand {
16
16
  transaction: import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
17
  truncate: import("@oclif/core/interfaces").BooleanFlag<boolean>;
18
18
  workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
+ exclude: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
20
+ filter: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
21
  force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
20
22
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
23
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
@@ -1,5 +1,6 @@
1
1
  import { Args, Flags, ux } from '@oclif/core';
2
2
  import * as yaml from 'js-yaml';
3
+ import { minimatch } from 'minimatch';
3
4
  import * as fs from 'node:fs';
4
5
  import * as os from 'node:os';
5
6
  import * as path from 'node:path';
@@ -46,6 +47,18 @@ Push without overwriting environment variables
46
47
  `,
47
48
  `$ xano workspace push ./my-workspace --truncate
48
49
  Truncate all table records before importing
50
+ `,
51
+ `$ xano workspace push ./my-workspace -f "**/func*"
52
+ Push only files matching the glob pattern
53
+ `,
54
+ `$ xano workspace push ./my-workspace -f "function/*" -f "table/*"
55
+ Push files matching multiple patterns
56
+ `,
57
+ `$ xano workspace push ./my-workspace -e "table/*"
58
+ Push all files except tables
59
+ `,
60
+ `$ xano workspace push ./my-workspace -f "function/*" -e "**/test*"
61
+ Push functions but exclude test files
49
62
  `,
50
63
  ];
51
64
  static flags = {
@@ -102,6 +115,18 @@ Truncate all table records before importing
102
115
  description: 'Workspace ID (optional if set in profile)',
103
116
  required: false,
104
117
  }),
118
+ exclude: Flags.string({
119
+ char: 'e',
120
+ description: 'Glob pattern to exclude files (e.g. "table/*", "**/test*"). Matched against relative paths from the push directory.',
121
+ multiple: true,
122
+ required: false,
123
+ }),
124
+ filter: Flags.string({
125
+ char: 'f',
126
+ description: 'Glob pattern to filter files (e.g. "**/func*", "table/*.xs"). Matched against relative paths from the push directory.',
127
+ multiple: true,
128
+ required: false,
129
+ }),
105
130
  force: Flags.boolean({
106
131
  default: false,
107
132
  description: 'Skip preview and confirmation prompt (for CI/CD pipelines)',
@@ -149,9 +174,33 @@ Truncate all table records before importing
149
174
  this.error(`Not a directory: ${inputDir}`);
150
175
  }
151
176
  // Collect all .xs files from the directory tree
152
- const files = this.collectFiles(inputDir);
177
+ const allFiles = this.collectFiles(inputDir);
178
+ let files = allFiles;
179
+ // Apply glob filter(s) if specified
180
+ if (flags.filter && flags.filter.length > 0) {
181
+ files = files.filter((f) => {
182
+ const rel = path.relative(inputDir, f);
183
+ return flags.filter.some((pattern) => minimatch(rel, pattern, { matchBase: true }));
184
+ });
185
+ this.log('');
186
+ this.log(` ${ux.colorize('dim', 'Filter:')} ${flags.filter.map((p) => ux.colorize('cyan', p)).join(', ')}`);
187
+ this.log(` ${ux.colorize('dim', 'Matched:')} ${ux.colorize('bold', String(files.length))} of ${allFiles.length} files`);
188
+ }
189
+ // Apply glob exclude(s) if specified
190
+ if (flags.exclude && flags.exclude.length > 0) {
191
+ const beforeCount = files.length;
192
+ files = files.filter((f) => {
193
+ const rel = path.relative(inputDir, f);
194
+ return !flags.exclude.some((pattern) => minimatch(rel, pattern, { matchBase: true }));
195
+ });
196
+ this.log('');
197
+ this.log(` ${ux.colorize('dim', 'Exclude:')} ${flags.exclude.map((p) => ux.colorize('cyan', p)).join(', ')}`);
198
+ this.log(` ${ux.colorize('dim', 'Kept:')} ${ux.colorize('bold', String(files.length))} of ${beforeCount} files (excluded ${beforeCount - files.length})`);
199
+ }
153
200
  if (files.length === 0) {
154
- this.error(`No .xs files found in ${args.directory}`);
201
+ this.error(flags.filter || flags.exclude
202
+ ? `No .xs files remain after ${[flags.filter ? `filter ${flags.filter.join(', ')}` : '', flags.exclude ? `exclude ${flags.exclude.join(', ')}` : ''].filter(Boolean).join(' and ')} in ${args.directory}`
203
+ : `No .xs files found in ${args.directory}`);
155
204
  }
156
205
  // Read each file and track file path alongside content
157
206
  const documentEntries = [];
@@ -385,7 +434,12 @@ Truncate all table records before importing
385
434
  return true;
386
435
  // For queries, operation name includes verb (e.g., "path/{id} DELETE")
387
436
  const opName = parsed.verb ? `${parsed.name} ${parsed.verb}` : parsed.name;
388
- return changedKeys.has(`${parsed.type}:${opName}`);
437
+ if (changedKeys.has(`${parsed.type}:${opName}`))
438
+ return true;
439
+ // Keep table documents that contain records when --records is active
440
+ if (flags.records && parsed.type === 'table' && /\bitems\s*=\s*\[/m.test(entry.content))
441
+ return true;
442
+ return false;
389
443
  });
390
444
  if (filteredEntries.length === 0) {
391
445
  this.log('No changes to push.');