@tinacms/cli 0.0.0-d888ad0-20241223044700 → 0.0.0-d9672bc-20250218033222

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.js CHANGED
@@ -17,6 +17,10 @@ var __copyProps = (to, from, except, desc) => {
17
17
  return to;
18
18
  };
19
19
  var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
20
24
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
21
25
  mod
22
26
  ));
@@ -31,9 +35,10 @@ module.exports = __toCommonJS(src_exports);
31
35
  var import_clipanion8 = require("clipanion");
32
36
 
33
37
  // package.json
34
- var version = "1.7.0";
38
+ var version = "1.8.4";
35
39
 
36
40
  // src/next/commands/dev-command/index.ts
41
+ var import_async_lock = __toESM(require("async-lock"));
37
42
  var import_clipanion2 = require("clipanion");
38
43
  var import_fs_extra6 = __toESM(require("fs-extra"));
39
44
  var import_path5 = __toESM(require("path"));
@@ -57,7 +62,10 @@ function isUnicodeSupported() {
57
62
  if (process.platform !== "win32") {
58
63
  return process.env.TERM !== "linux";
59
64
  }
60
- return Boolean(process.env.CI) || Boolean(process.env.WT_SESSION) || Boolean(process.env.TERMINUS_SUBLIME) || process.env.ConEmuTask === "{cmd::Cmder}" || process.env.TERM_PROGRAM === "Terminus-Sublime" || process.env.TERM_PROGRAM === "vscode" || process.env.TERM === "xterm-256color" || process.env.TERM === "alacritty" || process.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
65
+ return Boolean(process.env.CI) || Boolean(process.env.WT_SESSION) || // Windows Terminal
66
+ Boolean(process.env.TERMINUS_SUBLIME) || // Terminus (<0.2.27)
67
+ process.env.ConEmuTask === "{cmd::Cmder}" || // ConEmu and cmder
68
+ process.env.TERM_PROGRAM === "Terminus-Sublime" || process.env.TERM_PROGRAM === "vscode" || process.env.TERM === "xterm-256color" || process.env.TERM === "alacritty" || process.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
61
69
  }
62
70
 
63
71
  // src/logger/index.ts
@@ -212,7 +220,7 @@ var ConfigManager = class {
212
220
  this.generatedCachePath = import_path.default.join(
213
221
  this.generatedFolderPath,
214
222
  ".cache",
215
- String(new Date().getTime())
223
+ String((/* @__PURE__ */ new Date()).getTime())
216
224
  );
217
225
  this.generatedGraphQLGQLPath = import_path.default.join(
218
226
  this.generatedFolderPath,
@@ -399,6 +407,9 @@ var ConfigManager = class {
399
407
  }
400
408
  throw `No path provided to print`;
401
409
  }
410
+ /**
411
+ * Given a filepath without an extension, find the first match (eg. tsx, ts, jsx, js)
412
+ */
402
413
  async getPathWithExtension(filepath) {
403
414
  const extensions = ["tsx", "ts", "jsx", "js"];
404
415
  let result;
@@ -472,6 +483,7 @@ var ConfigManager = class {
472
483
  await esbuild.build({
473
484
  entryPoints: [outfile],
474
485
  bundle: true,
486
+ // Suppress warning about comparison with -0 from client module
475
487
  logLevel: "silent",
476
488
  platform: "node",
477
489
  outfile: outfile2,
@@ -578,19 +590,19 @@ var devHTML = (port) => `<!DOCTYPE html>
578
590
  window.$RefreshReg$ = () => {}
579
591
  window.$RefreshSig$ = () => (type) => type
580
592
  window.__vite_plugin_react_preamble_installed__ = true
581
- <\/script>
582
- <script type="module" src="http://localhost:${port}/@vite/client"><\/script>
593
+ </script>
594
+ <script type="module" src="http://localhost:${port}/@vite/client"></script>
583
595
  <script>
584
596
  function handleLoadError() {
585
597
  // Assets have failed to load
586
598
  document.getElementById('root').innerHTML = '${errorHTML}';
587
599
  }
588
- <\/script>
600
+ </script>
589
601
  <script
590
602
  type="module"
591
603
  src="http://localhost:${port}/src/main.tsx"
592
604
  onerror="handleLoadError()"
593
- ><\/script>
605
+ ></script>
594
606
  <body class="tina-tailwind">
595
607
  <div id="root"></div>
596
608
  </body>
@@ -636,6 +648,7 @@ var import_defaultTheme = __toESM(require("tailwindcss/defaultTheme.js"));
636
648
  var tinaTailwind = (spaPath, prebuildFilePath) => {
637
649
  return {
638
650
  name: "vite-plugin-tina",
651
+ // @ts-ignore
639
652
  config: (viteConfig) => {
640
653
  const plugins = [];
641
654
  const content = [
@@ -1019,15 +1032,33 @@ var createConfig = async ({
1019
1032
  dedupe: ["graphql", "tinacms", "react", "react-dom", "react-router-dom"]
1020
1033
  },
1021
1034
  define: {
1035
+ /**
1036
+ * Since we prebuild the config.ts, it's possible for modules to be loaded which make
1037
+ * use of `process`. The main scenario where this is an issue is when co-locating schema
1038
+ * definitions with source files, and specifically source files which impor from NextJS.
1039
+ *
1040
+ * Some examples of what NextJS uses for `process.env` are:
1041
+ * - `process.env.__NEXT_TRAILING_SLASH`
1042
+ * - `process.env.__NEXT_CROSS_ORIGIN`
1043
+ * - `process.env.__NEXT_I18N_SUPPORT`
1044
+ *
1045
+ * Also, interestingly some of the advice for handling this doesn't work, references to replacing
1046
+ * `process.env` with `{}` are problematic, because browsers don't understand the `{}.` syntax,
1047
+ * but node does. This was a surprise, but using `new Object()` seems to do the trick.
1048
+ */
1022
1049
  "process.env": `new Object(${JSON.stringify(publicEnv)})`,
1050
+ // Used by picomatch https://github.com/micromatch/picomatch/blob/master/lib/utils.js#L4
1023
1051
  "process.platform": `"${process.platform}"`,
1024
1052
  __API_URL__: `"${apiURL}"`,
1025
1053
  __BASE_PATH__: `"${((_e = (_d = configManager.config) == null ? void 0 : _d.build) == null ? void 0 : _e.basePath) || ""}"`,
1026
1054
  __TINA_GRAPHQL_VERSION__: version2
1027
1055
  },
1028
1056
  logLevel: "error",
1057
+ // Vite import warnings are noisy
1029
1058
  optimizeDeps: {
1030
1059
  force: true,
1060
+ // Not 100% sure why this isn't being picked up automatically, this works from within the monorepo
1061
+ // but breaks externally
1031
1062
  include: ["react/jsx-runtime", "react/jsx-dev-runtime"]
1032
1063
  },
1033
1064
  server: {
@@ -1035,6 +1066,7 @@ var createConfig = async ({
1035
1066
  watch: noWatch ? {
1036
1067
  ignored: ["**/*"]
1037
1068
  } : {
1069
+ // Ignore everything except for the alias fields we specified above
1038
1070
  ignored: [
1039
1071
  `${configManager.tinaFolderPath}/**/!(config.prebuild.jsx|_graphql.json)`
1040
1072
  ]
@@ -1050,8 +1082,13 @@ var createConfig = async ({
1050
1082
  rollupOptions
1051
1083
  },
1052
1084
  plugins: [
1085
+ /**
1086
+ * `splitVendorChunkPlugin` is needed because `tinacms` is quite large,
1087
+ * Vite's chunking strategy chokes on memory issues for smaller machines (ie. on CI).
1088
+ */
1053
1089
  (0, import_plugin_react.default)({
1054
1090
  babel: {
1091
+ // Supresses the warning [NOTE] babel The code generator has deoptimised the styling of
1055
1092
  compact: true
1056
1093
  }
1057
1094
  }),
@@ -1128,8 +1165,7 @@ var createMediaRouter = (config2) => {
1128
1165
  };
1129
1166
  var parseMediaFolder = (str) => {
1130
1167
  let returnString = str;
1131
- if (returnString.startsWith("/"))
1132
- returnString = returnString.substr(1);
1168
+ if (returnString.startsWith("/")) returnString = returnString.substr(1);
1133
1169
  if (returnString.endsWith("/"))
1134
1170
  returnString = returnString.substr(0, returnString.length - 1);
1135
1171
  return returnString;
@@ -1294,7 +1330,8 @@ var devServerEndPointsPlugin = ({
1294
1330
  configManager,
1295
1331
  apiURL,
1296
1332
  database,
1297
- searchIndex
1333
+ searchIndex,
1334
+ databaseLock
1298
1335
  }) => {
1299
1336
  const plug = {
1300
1337
  name: "graphql-endpoints",
@@ -1338,14 +1375,17 @@ var devServerEndPointsPlugin = ({
1338
1375
  }
1339
1376
  if (req.url === "/graphql") {
1340
1377
  const { query, variables } = req.body;
1341
- const result = await (0, import_graphql.resolve)({
1342
- config: {
1343
- useRelativeMedia: true
1344
- },
1345
- database,
1346
- query,
1347
- variables,
1348
- verbose: false
1378
+ let result;
1379
+ await databaseLock(async () => {
1380
+ result = await (0, import_graphql.resolve)({
1381
+ config: {
1382
+ useRelativeMedia: true
1383
+ },
1384
+ database,
1385
+ query,
1386
+ variables,
1387
+ verbose: false
1388
+ });
1349
1389
  });
1350
1390
  res.end(JSON.stringify(result));
1351
1391
  return;
@@ -1396,6 +1436,7 @@ function viteTransformExtension({
1396
1436
  return {
1397
1437
  code: res.code,
1398
1438
  map: null
1439
+ // TODO:
1399
1440
  };
1400
1441
  }
1401
1442
  }
@@ -1403,10 +1444,16 @@ function viteTransformExtension({
1403
1444
  }
1404
1445
 
1405
1446
  // src/next/commands/dev-command/server/index.ts
1406
- var createDevServer = async (configManager, database, searchIndex, apiURL, noWatch) => {
1447
+ var createDevServer = async (configManager, database, searchIndex, apiURL, noWatch, databaseLock) => {
1407
1448
  const plugins = [
1408
1449
  transformTsxPlugin({ configManager }),
1409
- devServerEndPointsPlugin({ apiURL, configManager, database, searchIndex }),
1450
+ devServerEndPointsPlugin({
1451
+ apiURL,
1452
+ configManager,
1453
+ database,
1454
+ searchIndex,
1455
+ databaseLock
1456
+ }),
1410
1457
  viteTransformExtension()
1411
1458
  ];
1412
1459
  return (0, import_vite3.createServer)(
@@ -1416,6 +1463,14 @@ var createDevServer = async (configManager, database, searchIndex, apiURL, noWat
1416
1463
  apiURL,
1417
1464
  plugins,
1418
1465
  noWatch,
1466
+ /**
1467
+ * Ensure Vite's import scan uses the spaMainPath as the input
1468
+ * so it properly finds everything. This is for dev only, and when
1469
+ * running the server outside of this monorepo vite fails to find
1470
+ * and optimize the imports, so you get errors about it not being
1471
+ * able to find an export from a module, and it's always a CJS
1472
+ * module that Vite would usually transform to an ES module.
1473
+ */
1419
1474
  rollupOptions: {
1420
1475
  input: configManager.spaMainPath,
1421
1476
  onwarn(warning, warn) {
@@ -1541,6 +1596,7 @@ var GenericSdkVisitor = class extends import_visitor_plugin_common.ClientSideBas
1541
1596
  node,
1542
1597
  documentVariableName,
1543
1598
  operationType,
1599
+ // This is the only line that is different
1544
1600
  operationResultType: `{data: ${operationResultType}, errors?: { message: string, locations: { line: number, column: number }[], path: string[] }[], variables: ${operationVariablesTypes}, query: string}`,
1545
1601
  operationVariablesTypes
1546
1602
  });
@@ -1589,6 +1645,8 @@ var plugin = (schema, documents, config2) => {
1589
1645
  const visitor = new GenericSdkVisitor(schema, allFragments, config2);
1590
1646
  const visitorResult = (0, import_graphql3.visit)(allAst, { leave: visitor });
1591
1647
  return {
1648
+ // We will take care of imports
1649
+ // prepend: visitor.getImports(),
1592
1650
  content: [
1593
1651
  visitor.fragments,
1594
1652
  ...visitorResult.definitions.filter((t) => typeof t === "string"),
@@ -1604,6 +1662,7 @@ var generateTypes = async (schema, queryPathGlob = process.cwd(), fragDocPath =
1604
1662
  docs = await loadGraphQLDocuments(queryPathGlob);
1605
1663
  fragDocs = await loadGraphQLDocuments(fragDocPath);
1606
1664
  const res = await (0, import_core.codegen)({
1665
+ // Filename is not used. This is because the typescript plugin returns a string instead of writing to a file.
1607
1666
  filename: process.cwd(),
1608
1667
  schema: (0, import_graphql5.parse)((0, import_graphql5.printSchema)(schema)),
1609
1668
  documents: [...docs, ...fragDocs],
@@ -1638,9 +1697,12 @@ var loadGraphQLDocuments = async (globPath) => {
1638
1697
  loaders: [new import_graphql_file_loader.GraphQLFileLoader()]
1639
1698
  });
1640
1699
  } catch (e) {
1641
- if ((e.message || "").includes(
1642
- "Unable to find any GraphQL type definitions for the following pointers:"
1643
- )) {
1700
+ if (
1701
+ // https://www.graphql-tools.com/docs/documents-loading#no-files-found
1702
+ (e.message || "").includes(
1703
+ "Unable to find any GraphQL type definitions for the following pointers:"
1704
+ )
1705
+ ) {
1644
1706
  } else {
1645
1707
  throw e;
1646
1708
  }
@@ -1802,12 +1864,9 @@ var Codegen = class {
1802
1864
  const baseUrl = ((_d = this.configManager.config.tinaioConfig) == null ? void 0 : _d.contentApiUrlOverride) || `https://${TINA_HOST}`;
1803
1865
  if ((!branch || !clientId || !token) && !this.port && !this.configManager.config.contentApiUrlOverride) {
1804
1866
  const missing = [];
1805
- if (!branch)
1806
- missing.push("branch");
1807
- if (!clientId)
1808
- missing.push("clientId");
1809
- if (!token)
1810
- missing.push("token");
1867
+ if (!branch) missing.push("branch");
1868
+ if (!clientId) missing.push("clientId");
1869
+ if (!token) missing.push("token");
1811
1870
  throw new Error(
1812
1871
  `Client not configured properly. Missing ${missing.join(
1813
1872
  ", "
@@ -1961,7 +2020,11 @@ schema {
1961
2020
  }
1962
2021
  };
1963
2022
  var maybeWarnFragmentSize = async (filepath) => {
1964
- if ((await import_fs_extra4.default.stat(filepath)).size > 100 * 1024) {
2023
+ if (
2024
+ // is the file bigger than 100kb?
2025
+ (await import_fs_extra4.default.stat(filepath)).size > // convert to 100 kb to bytes
2026
+ 100 * 1024
2027
+ ) {
1965
2028
  console.warn(
1966
2029
  "Warning: frags.gql is very large (>100kb). Consider setting the reference depth to 1 or 0. See code snippet below."
1967
2030
  );
@@ -1989,6 +2052,7 @@ var import_many_level = require("many-level");
1989
2052
  var import_memory_level = require("memory-level");
1990
2053
  var createDBServer = (port) => {
1991
2054
  const levelHost = new import_many_level.ManyLevelHost(
2055
+ // @ts-ignore
1992
2056
  new import_memory_level.MemoryLevel({
1993
2057
  valueEncoding: "json"
1994
2058
  })
@@ -2139,7 +2203,9 @@ var BaseCommand = class extends import_clipanion.Command {
2139
2203
  let subProc;
2140
2204
  if (this.subCommand) {
2141
2205
  subProc = await startSubprocess2({ command: this.subCommand });
2142
- logger.info(`Starting subprocess: ${import_chalk4.default.cyan(this.subCommand)}`);
2206
+ logger.info(
2207
+ `Running web application with command: ${import_chalk4.default.cyan(this.subCommand)}`
2208
+ );
2143
2209
  }
2144
2210
  function exitHandler(options, exitCode) {
2145
2211
  if (subProc) {
@@ -2263,6 +2329,7 @@ var import_search = require("@tinacms/search");
2263
2329
  var DevCommand = class extends BaseCommand {
2264
2330
  constructor() {
2265
2331
  super(...arguments);
2332
+ // NOTE: camelCase commands for string options don't work if there's an `=` used https://github.com/arcanis/clipanion/issues/141
2266
2333
  this.watchFolders = import_clipanion2.Option.String("-w,--watchFolders", {
2267
2334
  description: "DEPRECATED - a list of folders (relative to where this is being run) that the cli will watch for changes"
2268
2335
  });
@@ -2272,6 +2339,7 @@ var DevCommand = class extends BaseCommand {
2272
2339
  this.outputSearchIndexPath = import_clipanion2.Option.String("--outputSearchIndexPath", {
2273
2340
  description: "Path to write the search index to"
2274
2341
  });
2342
+ this.indexingLock = new import_async_lock.default();
2275
2343
  }
2276
2344
  async catch(error) {
2277
2345
  logger.error("Error occured during tinacms dev");
@@ -2292,10 +2360,13 @@ var DevCommand = class extends BaseCommand {
2292
2360
  rootPath: this.rootPath,
2293
2361
  legacyNoSDK: this.noSDK
2294
2362
  });
2295
- logger.info("Starting Tina Dev Server");
2363
+ logger.info("\u{1F999} TinaCMS Dev Server is initializing...");
2296
2364
  this.logDeprecationWarnings();
2297
2365
  createDBServer(Number(this.datalayerPort));
2298
2366
  let database = null;
2367
+ const dbLock = async (fn) => {
2368
+ return this.indexingLock.acquire("Key", fn);
2369
+ };
2299
2370
  const setup = async ({ firstTime }) => {
2300
2371
  try {
2301
2372
  await configManager.processConfig();
@@ -2346,9 +2417,6 @@ var DevCommand = class extends BaseCommand {
2346
2417
  await import_fs_extra6.default.outputFile(filePath, tinaLockContent);
2347
2418
  }
2348
2419
  }
2349
- if (!this.noWatch) {
2350
- this.watchQueries(configManager, async () => await codegen2.execute());
2351
- }
2352
2420
  await this.indexContentWithSpinner({
2353
2421
  database,
2354
2422
  graphQLSchema: graphQLSchema2,
@@ -2358,6 +2426,13 @@ var DevCommand = class extends BaseCommand {
2358
2426
  if (!firstTime) {
2359
2427
  logger.error("Re-index complete");
2360
2428
  }
2429
+ if (!this.noWatch) {
2430
+ this.watchQueries(
2431
+ configManager,
2432
+ dbLock,
2433
+ async () => await codegen2.execute()
2434
+ );
2435
+ }
2361
2436
  return { apiURL: apiURL2, database, graphQLSchema: graphQLSchema2, tinaSchema: tinaSchema2 };
2362
2437
  } catch (e) {
2363
2438
  logger.error(`
@@ -2392,14 +2467,6 @@ ${dangerText(e.message)}
2392
2467
  tokenSplitRegex: (_d = (_c = configManager.config.search) == null ? void 0 : _c.tina) == null ? void 0 : _d.tokenSplitRegex
2393
2468
  });
2394
2469
  await searchIndexClient.onStartIndexing();
2395
- const server = await createDevServer(
2396
- configManager,
2397
- database,
2398
- searchIndexClient.searchIndex,
2399
- apiURL,
2400
- this.noWatch
2401
- );
2402
- await server.listen(Number(this.port));
2403
2470
  const searchIndexer = new import_search.SearchIndexer({
2404
2471
  batchSize: ((_e = configManager.config.search) == null ? void 0 : _e.indexBatchSize) || 100,
2405
2472
  bridge: new import_graphql10.FilesystemBridge(
@@ -2425,12 +2492,26 @@ ${dangerText(e.message)}
2425
2492
  this.watchContentFiles(
2426
2493
  configManager,
2427
2494
  database,
2495
+ dbLock,
2428
2496
  configManager.config.search && searchIndexer
2429
2497
  );
2498
+ }
2499
+ const server = await createDevServer(
2500
+ configManager,
2501
+ database,
2502
+ searchIndexClient.searchIndex,
2503
+ apiURL,
2504
+ this.noWatch,
2505
+ dbLock
2506
+ );
2507
+ await server.listen(Number(this.port));
2508
+ if (!this.noWatch) {
2430
2509
  import_chokidar.default.watch(configManager.watchList).on("change", async () => {
2431
- logger.info(`Tina config change detected, rebuilding`);
2432
- await setup({ firstTime: false });
2433
- server.ws.send({ type: "full-reload", path: "*" });
2510
+ await dbLock(async () => {
2511
+ logger.info(`Tina config change detected, rebuilding`);
2512
+ await setup({ firstTime: false });
2513
+ server.ws.send({ type: "full-reload", path: "*" });
2514
+ });
2434
2515
  });
2435
2516
  }
2436
2517
  const subItems = [];
@@ -2443,7 +2524,7 @@ ${dangerText(e.message)}
2443
2524
  const summaryItems = [
2444
2525
  {
2445
2526
  emoji: "\u{1F999}",
2446
- heading: "Tina Config",
2527
+ heading: "TinaCMS URLs",
2447
2528
  subItems: [
2448
2529
  {
2449
2530
  key: "CMS",
@@ -2478,14 +2559,28 @@ ${dangerText(e.message)}
2478
2559
  });
2479
2560
  }
2480
2561
  summary({
2481
- heading: "Tina Dev Server is running...",
2562
+ heading: "\u2705 \u{1F999} TinaCMS Dev Server is active:",
2482
2563
  items: [
2483
2564
  ...summaryItems
2565
+ // {
2566
+ // emoji: '📚',
2567
+ // heading: 'Useful links',
2568
+ // subItems: [
2569
+ // {
2570
+ // key: 'Custom queries',
2571
+ // value: 'https://tina.io/querying',
2572
+ // },
2573
+ // {
2574
+ // key: 'Visual editing',
2575
+ // value: 'https://tina.io/visual-editing',
2576
+ // },
2577
+ // ],
2578
+ // },
2484
2579
  ]
2485
2580
  });
2486
2581
  await this.startSubCommand();
2487
2582
  }
2488
- watchContentFiles(configManager, database, searchIndexer) {
2583
+ watchContentFiles(configManager, database, databaseLock, searchIndexer) {
2489
2584
  const collectionContentFiles = [];
2490
2585
  configManager.config.schema.collections.forEach((collection) => {
2491
2586
  const collectionGlob = `${import_path5.default.join(
@@ -2501,39 +2596,42 @@ ${dangerText(e.message)}
2501
2596
  if (!ready) {
2502
2597
  return;
2503
2598
  }
2504
- const pathFromRoot = configManager.printContentRelativePath(addedFile);
2505
- await database.indexContentByPaths([pathFromRoot]).catch(console.error);
2506
- if (searchIndexer) {
2507
- await searchIndexer.indexContentByPaths([pathFromRoot]).catch(console.error);
2508
- }
2599
+ await databaseLock(async () => {
2600
+ const pathFromRoot = configManager.printContentRelativePath(addedFile);
2601
+ await database.indexContentByPaths([pathFromRoot]).catch(console.error);
2602
+ if (searchIndexer) {
2603
+ await searchIndexer.indexContentByPaths([pathFromRoot]).catch(console.error);
2604
+ }
2605
+ });
2509
2606
  }).on("change", async (changedFile) => {
2510
2607
  const pathFromRoot = configManager.printContentRelativePath(changedFile);
2511
- await database.indexContentByPaths([pathFromRoot]).catch(console.error);
2512
- if (searchIndexer) {
2513
- await searchIndexer.indexContentByPaths([pathFromRoot]).catch(console.error);
2514
- }
2608
+ await databaseLock(async () => {
2609
+ await database.indexContentByPaths([pathFromRoot]).catch(console.error);
2610
+ if (searchIndexer) {
2611
+ await searchIndexer.indexContentByPaths([pathFromRoot]).catch(console.error);
2612
+ }
2613
+ });
2515
2614
  }).on("unlink", async (removedFile) => {
2516
2615
  const pathFromRoot = configManager.printContentRelativePath(removedFile);
2517
- await database.deleteContentByPaths([pathFromRoot]).catch(console.error);
2518
- if (searchIndexer) {
2519
- await searchIndexer.deleteIndexContent([pathFromRoot]).catch(console.error);
2520
- }
2616
+ await databaseLock(async () => {
2617
+ await database.deleteContentByPaths([pathFromRoot]).catch(console.error);
2618
+ if (searchIndexer) {
2619
+ await searchIndexer.deleteIndexContent([pathFromRoot]).catch(console.error);
2620
+ }
2621
+ });
2521
2622
  });
2522
2623
  }
2523
- watchQueries(configManager, callback) {
2524
- let ready = false;
2525
- import_chokidar.default.watch(configManager.userQueriesAndFragmentsGlob).on("ready", () => {
2526
- ready = true;
2527
- }).on("add", async (addedFile) => {
2528
- await callback();
2529
- }).on("change", async (changedFile) => {
2530
- await callback();
2531
- }).on("unlink", async (removedFile) => {
2532
- await callback();
2533
- });
2624
+ watchQueries(configManager, databaseLock, callback) {
2625
+ const executeCallback = async (_) => {
2626
+ await databaseLock(async () => {
2627
+ await callback();
2628
+ });
2629
+ };
2630
+ import_chokidar.default.watch(configManager.userQueriesAndFragmentsGlob).on("add", executeCallback).on("change", executeCallback).on("unlink", executeCallback);
2534
2631
  }
2535
2632
  };
2536
2633
  DevCommand.paths = [["dev"], ["server:start"]];
2634
+ // Prevent indexes and reads occurring at once
2537
2635
  DevCommand.usage = import_clipanion2.Command.Usage({
2538
2636
  category: `Commands`,
2539
2637
  description: `Builds Tina and starts the dev server`,
@@ -2723,6 +2821,9 @@ var BuildCommand = class extends BaseCommand {
2723
2821
  this.tinaGraphQLVersion = import_clipanion3.Option.String("--tina-graphql-version", {
2724
2822
  description: "Specify the version of @tinacms/graphql to use (defaults to latest)"
2725
2823
  });
2824
+ /**
2825
+ * This option allows the user to skip the tina cloud checks if they want to. This could be useful for mismatched GraphQL versions or if they want to build only using the local client and never connect to Tina Cloud
2826
+ */
2726
2827
  this.skipCloudChecks = import_clipanion3.Option.Boolean("--skip-cloud-checks", false, {
2727
2828
  description: "Skips checking the provided cloud config."
2728
2829
  });
@@ -2828,7 +2929,8 @@ ${dangerText(e.message)}
2828
2929
  database,
2829
2930
  null,
2830
2931
  apiURL,
2831
- true
2932
+ true,
2933
+ (lockedFn) => lockedFn()
2832
2934
  );
2833
2935
  await server.listen(Number(this.port));
2834
2936
  console.log("server listening on port", this.port);
@@ -3463,6 +3565,7 @@ var auditDocuments = async (args) => {
3463
3565
  logger.error(import_chalk5.default.red(err.message));
3464
3566
  if (err.originalError.originalError) {
3465
3567
  logger.error(
3568
+ // @ts-ignore FIXME: this doesn't seem right
3466
3569
  import_chalk5.default.red(` ${err.originalError.originalError.message}`)
3467
3570
  );
3468
3571
  }
@@ -3668,7 +3771,9 @@ var detectEnvironment = async ({
3668
3771
  const usingSrc = import_fs_extra8.default.pathExistsSync(import_path7.default.join(baseDir, "src")) && (import_fs_extra8.default.pathExistsSync(import_path7.default.join(baseDir, "src", "app")) || import_fs_extra8.default.pathExistsSync(import_path7.default.join(baseDir, "src", "pages")));
3669
3772
  const tinaFolder = import_path7.default.join(baseDir, "tina");
3670
3773
  const tinaConfigExists = Boolean(
3671
- await import_fs_extra8.default.pathExists(tinaFolder) && (await import_fs_extra8.default.readdir(tinaFolder)).find((x) => x.includes("config"))
3774
+ // Does the tina folder exist?
3775
+ await import_fs_extra8.default.pathExists(tinaFolder) && // Does the tina folder contain a config file?
3776
+ (await import_fs_extra8.default.readdir(tinaFolder)).find((x) => x.includes("config"))
3672
3777
  );
3673
3778
  const pagesDir = [baseDir, usingSrc ? "src" : false, "pages"].filter(
3674
3779
  Boolean
@@ -3903,6 +4008,7 @@ var supportedDatabaseAdapters = {
3903
4008
  {
3904
4009
  from: "mongodb",
3905
4010
  imported: [],
4011
+ // not explicitly imported
3906
4012
  packageName: "mongodb"
3907
4013
  }
3908
4014
  ]
@@ -3975,6 +4081,10 @@ var chooseDatabaseAdapter = async ({
3975
4081
  title: "MongoDB",
3976
4082
  value: "mongodb"
3977
4083
  }
4084
+ // {
4085
+ // title: "I'll create my own database adapter",
4086
+ // value: 'other',
4087
+ // },
3978
4088
  ]
3979
4089
  }
3980
4090
  ]);
@@ -4252,6 +4362,7 @@ async function configure(env, opts) {
4252
4362
  packageManager,
4253
4363
  forestryMigrate: false,
4254
4364
  isLocalEnvVarName: "TINA_PUBLIC_IS_LOCAL",
4365
+ // TODO: give this a better default
4255
4366
  typescript: false
4256
4367
  };
4257
4368
  if (config2.framework.name === "next") {
@@ -4355,15 +4466,25 @@ var import_js_yaml = __toESM(require("js-yaml"));
4355
4466
  var import_zod = __toESM(require("zod"));
4356
4467
 
4357
4468
  // src/cmds/forestry-migrate/util/errorSingleton.ts
4358
- var ErrorSingleton = class {
4469
+ var ErrorSingleton = class _ErrorSingleton {
4470
+ /**
4471
+ * The Singleton's constructor should always be private to prevent direct
4472
+ * construction calls with the `new` operator.
4473
+ */
4359
4474
  constructor() {
4360
4475
  }
4476
+ /**
4477
+ * The static method that controls the access to the singleton instance.
4478
+ *
4479
+ * This implementation let you subclass the Singleton class while keeping
4480
+ * just one instance of each subclass around.
4481
+ */
4361
4482
  static getInstance() {
4362
- if (!ErrorSingleton.instance) {
4363
- ErrorSingleton.instance = new ErrorSingleton();
4364
- ErrorSingleton.instance.collectionNameErrors = [];
4483
+ if (!_ErrorSingleton.instance) {
4484
+ _ErrorSingleton.instance = new _ErrorSingleton();
4485
+ _ErrorSingleton.instance.collectionNameErrors = [];
4365
4486
  }
4366
- return ErrorSingleton.instance;
4487
+ return _ErrorSingleton.instance;
4367
4488
  }
4368
4489
  addErrorName(error) {
4369
4490
  this.collectionNameErrors.push(error);
@@ -4406,8 +4527,7 @@ var makeFieldsWithInternalCode = ({
4406
4527
  if (hasBody) {
4407
4528
  return [bodyField, `__TINA_INTERNAL__:::...${field}():::`];
4408
4529
  } else {
4409
- if (spread)
4410
- return `__TINA_INTERNAL__:::...${field}():::`;
4530
+ if (spread) return `__TINA_INTERNAL__:::...${field}():::`;
4411
4531
  return `__TINA_INTERNAL__:::${field}():::`;
4412
4532
  }
4413
4533
  };
@@ -4491,6 +4611,7 @@ var forestryConfigSchema = import_zod.default.object({
4491
4611
  )
4492
4612
  });
4493
4613
  var forestryFieldWithoutField = import_zod.default.object({
4614
+ // TODO: maybe better type this?
4494
4615
  type: import_zod.default.union([
4495
4616
  import_zod.default.literal("text"),
4496
4617
  import_zod.default.literal("datetime"),
@@ -4514,6 +4635,7 @@ var forestryFieldWithoutField = import_zod.default.object({
4514
4635
  default: import_zod.default.any().optional(),
4515
4636
  template: import_zod.default.string().optional(),
4516
4637
  config: import_zod.default.object({
4638
+ // min and max are used for lists
4517
4639
  min: import_zod.default.number().optional().nullable(),
4518
4640
  max: import_zod.default.number().optional().nullable(),
4519
4641
  required: import_zod.default.boolean().optional().nullable(),
@@ -4527,6 +4649,7 @@ var forestryFieldWithoutField = import_zod.default.object({
4527
4649
  import_zod.default.literal("pages"),
4528
4650
  import_zod.default.literal("documents"),
4529
4651
  import_zod.default.literal("simple"),
4652
+ // TODO: I want to ignore this key if its invalid
4530
4653
  import_zod.default.string()
4531
4654
  ]).optional().nullable(),
4532
4655
  section: import_zod.default.string().optional().nullable()
@@ -4562,6 +4685,7 @@ var transformForestryFieldsToTinaFields = ({
4562
4685
  }
4563
4686
  let field;
4564
4687
  switch (forestryField2.type) {
4688
+ // Single filed types
4565
4689
  case "text":
4566
4690
  field = {
4567
4691
  type: "string",
@@ -4641,6 +4765,7 @@ var transformForestryFieldsToTinaFields = ({
4641
4765
  );
4642
4766
  }
4643
4767
  break;
4768
+ // List Types
4644
4769
  case "list":
4645
4770
  field = {
4646
4771
  type: "string",
@@ -4663,6 +4788,7 @@ var transformForestryFieldsToTinaFields = ({
4663
4788
  }
4664
4789
  };
4665
4790
  break;
4791
+ // Object (Group) types
4666
4792
  case "field_group":
4667
4793
  field = {
4668
4794
  type: "object",
@@ -4703,6 +4829,7 @@ var transformForestryFieldsToTinaFields = ({
4703
4829
  });
4704
4830
  const fieldsString = stringifyLabelWithField(template2.label);
4705
4831
  const t = {
4832
+ // @ts-ignore
4706
4833
  fields: makeFieldsWithInternalCode({
4707
4834
  hasBody: false,
4708
4835
  field: fieldsString
@@ -4740,6 +4867,7 @@ var transformForestryFieldsToTinaFields = ({
4740
4867
  spread: true
4741
4868
  });
4742
4869
  tinaFields.push(
4870
+ // @ts-ignore
4743
4871
  field2
4744
4872
  );
4745
4873
  break;
@@ -4799,6 +4927,7 @@ var parseSections = ({ val }) => {
4799
4927
 
4800
4928
  // src/cmds/forestry-migrate/index.ts
4801
4929
  var BODY_FIELD = {
4930
+ // This is the body field
4802
4931
  type: "rich-text",
4803
4932
  name: "body",
4804
4933
  label: "Body of Document",
@@ -4857,8 +4986,7 @@ var generateAllTemplates = async ({
4857
4986
  };
4858
4987
  var generateCollectionFromForestrySection = (args) => {
4859
4988
  const { section, templateMap } = args;
4860
- if (section.read_only)
4861
- return;
4989
+ if (section.read_only) return;
4862
4990
  let format3 = "md";
4863
4991
  if (section.new_doc_ext) {
4864
4992
  const ext = checkExt(section.new_doc_ext);
@@ -4925,12 +5053,14 @@ var generateCollectionFromForestrySection = (args) => {
4925
5053
  if (((forestryTemplates == null ? void 0 : forestryTemplates.length) || 0) > 1) {
4926
5054
  c = {
4927
5055
  ...baseCollection,
5056
+ // @ts-expect-error
4928
5057
  templates: forestryTemplates.map((tem) => {
4929
5058
  const currentTemplate = templateMap.get(tem);
4930
5059
  const fieldsString = stringifyLabelWithField(
4931
5060
  currentTemplate.templateObj.label
4932
5061
  );
4933
5062
  return {
5063
+ // fields: [BODY_FIELD],
4934
5064
  fields: makeFieldsWithInternalCode({
4935
5065
  hasBody,
4936
5066
  field: fieldsString,
@@ -4948,6 +5078,8 @@ var generateCollectionFromForestrySection = (args) => {
4948
5078
  const fieldsString = stringifyLabelWithField(template.templateObj.label);
4949
5079
  c = {
4950
5080
  ...baseCollection,
5081
+ // fields: [BODY_FIELD],
5082
+ // @ts-expect-error
4951
5083
  fields: makeFieldsWithInternalCode({
4952
5084
  field: fieldsString,
4953
5085
  hasBody,
@@ -5695,6 +5827,7 @@ var makeImportsVisitor = (sourceFile, importMap) => (ctx) => (node) => {
5695
5827
  ) : [];
5696
5828
  const newImports = [
5697
5829
  .../* @__PURE__ */ new Set([
5830
+ // we use Set to remove duplicates
5698
5831
  ...existingImports,
5699
5832
  ...imports
5700
5833
  ])
@@ -5849,6 +5982,7 @@ var addSelfHostedTinaAuthToConfig = async (config2, configFile) => {
5849
5982
  );
5850
5983
  const { configImports, configAuthProviderClass, extraTinaCollections } = config2.authProvider;
5851
5984
  const importMap = {
5985
+ // iterate over configImports and add them to the import map
5852
5986
  ...configImports.reduce((acc, { from, imported }) => {
5853
5987
  acc[from] = imported;
5854
5988
  return acc;
@@ -6023,7 +6157,13 @@ async function apply({
6023
6157
  config: config2
6024
6158
  });
6025
6159
  }
6026
- if (env.tinaConfigExists && params.isBackendInit && config2.hosting === "self-host" && (((_a = config2.authProvider) == null ? void 0 : _a.name) || "") !== "tina-cloud") {
6160
+ if (
6161
+ // if the config was just generated we do not need to update the config file because it will be generated correctly
6162
+ env.tinaConfigExists && // Are we running tinacms init backend
6163
+ params.isBackendInit && // Do the user choose the 'self-host' option
6164
+ config2.hosting === "self-host" && // the user did not choose the 'tina-cloud' auth provider
6165
+ (((_a = config2.authProvider) == null ? void 0 : _a.name) || "") !== "tina-cloud"
6166
+ ) {
6027
6167
  await addSelfHostedTinaAuthToConfig(config2, env.generatedFiles["config"]);
6028
6168
  }
6029
6169
  logNextSteps({
@@ -6316,6 +6456,7 @@ var other = ({ packageManager }) => {
6316
6456
  const packageManagers = {
6317
6457
  pnpm: `pnpm`,
6318
6458
  npm: `npx`,
6459
+ // npx is the way to run executables that aren't in your "scripts"
6319
6460
  yarn: `yarn`
6320
6461
  };
6321
6462
  return `${packageManagers[packageManager]} tinacms dev -c "<your dev command>"`;
@@ -6328,6 +6469,7 @@ var frameworkDevCmds = {
6328
6469
  const packageManagers = {
6329
6470
  pnpm: `pnpm`,
6330
6471
  npm: `npm run`,
6472
+ // npx is the way to run executables that aren't in your "scripts"
6331
6473
  yarn: `yarn`
6332
6474
  };
6333
6475
  return `${packageManagers[packageManager]} dev`;
@@ -6585,5 +6727,3 @@ cli.register(import_clipanion8.Builtins.DefinitionsCommand);
6585
6727
  cli.register(import_clipanion8.Builtins.HelpCommand);
6586
6728
  cli.register(import_clipanion8.Builtins.VersionCommand);
6587
6729
  var src_default = cli;
6588
- // Annotate the CommonJS export names for ESM import in node:
6589
- 0 && (module.exports = {});
@@ -1,3 +1,4 @@
1
+ import AsyncLock from 'async-lock';
1
2
  import { Database } from '@tinacms/graphql';
2
3
  import { ConfigManager } from '../../config-manager';
3
4
  import { BaseCommand } from '../baseCommands';
@@ -7,10 +8,11 @@ export declare class DevCommand extends BaseCommand {
7
8
  watchFolders: string;
8
9
  noWatch: boolean;
9
10
  outputSearchIndexPath: string;
11
+ indexingLock: AsyncLock;
10
12
  static usage: import("clipanion").Usage;
11
13
  catch(error: any): Promise<void>;
12
14
  logDeprecationWarnings(): void;
13
15
  execute(): Promise<number | void>;
14
- watchContentFiles(configManager: ConfigManager, database: Database, searchIndexer?: SearchIndexer): void;
15
- watchQueries(configManager: ConfigManager, callback: () => Promise<string>): void;
16
+ watchContentFiles(configManager: ConfigManager, database: Database, databaseLock: (fn: () => Promise<void>) => Promise<void>, searchIndexer?: SearchIndexer): void;
17
+ watchQueries(configManager: ConfigManager, databaseLock: (fn: () => Promise<void>) => Promise<void>, callback: () => Promise<string>): void;
16
18
  }
@@ -1,3 +1,3 @@
1
1
  import type { Database } from '@tinacms/graphql';
2
2
  import { ConfigManager } from '../../../config-manager';
3
- export declare const createDevServer: (configManager: ConfigManager, database: Database, searchIndex: any, apiURL: string, noWatch: boolean) => Promise<import("vite").ViteDevServer>;
3
+ export declare const createDevServer: (configManager: ConfigManager, database: Database, searchIndex: any, apiURL: string, noWatch: boolean, databaseLock: (fn: () => Promise<void>) => Promise<void>) => Promise<import("vite").ViteDevServer>;
@@ -7,11 +7,12 @@ import type { ConfigManager } from '../config-manager';
7
7
  export declare const transformTsxPlugin: ({ configManager: _configManager, }: {
8
8
  configManager: ConfigManager;
9
9
  }) => Plugin;
10
- export declare const devServerEndPointsPlugin: ({ configManager, apiURL, database, searchIndex, }: {
10
+ export declare const devServerEndPointsPlugin: ({ configManager, apiURL, database, searchIndex, databaseLock, }: {
11
11
  apiURL: string;
12
12
  database: Database;
13
13
  configManager: ConfigManager;
14
14
  searchIndex: any;
15
+ databaseLock: (fn: () => Promise<void>) => Promise<void>;
15
16
  }) => Plugin;
16
17
  export interface ViteSvgrOptions {
17
18
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinacms/cli",
3
- "version": "0.0.0-d888ad0-20241223044700",
3
+ "version": "0.0.0-d9672bc-20250218033222",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
6
6
  "files": [
@@ -55,6 +55,7 @@
55
55
  "@tailwindcss/typography": "^0.5.15",
56
56
  "@vitejs/plugin-react": "3.1.0",
57
57
  "altair-express-middleware": "^7.3.6",
58
+ "async-lock": "^1.4.1",
58
59
  "auto-bind": "^4.0.0",
59
60
  "body-parser": "^1.20.3",
60
61
  "busboy": "^1.6.0",
@@ -84,12 +85,12 @@
84
85
  "vite": "^4.5.5",
85
86
  "yup": "^1.4.0",
86
87
  "zod": "^3.23.8",
87
- "@tinacms/app": "2.1.14",
88
+ "@tinacms/graphql": "0.0.0-d9672bc-20250218033222",
88
89
  "@tinacms/metrics": "1.0.8",
89
- "@tinacms/schema-tools": "1.6.9",
90
- "@tinacms/graphql": "1.5.9",
91
- "@tinacms/search": "1.0.36",
92
- "tinacms": "2.5.2"
90
+ "@tinacms/search": "0.0.0-d9672bc-20250218033222",
91
+ "@tinacms/app": "0.0.0-d9672bc-20250218033222",
92
+ "tinacms": "0.0.0-d9672bc-20250218033222",
93
+ "@tinacms/schema-tools": "0.0.0-d9672bc-20250218033222"
93
94
  },
94
95
  "publishConfig": {
95
96
  "registry": "https://registry.npmjs.org"