@akanjs/cli 2.3.11 → 2.3.12-rc.0

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.
@@ -721,6 +721,7 @@ var WORKSPACE_BARREL_FACETS = ["ui", "webkit", "common", "client", "server"];
721
721
  var SSR_RUNTIME_PACKAGES = ["react", "react-dom", "react-server-dom-webpack"];
722
722
  var NATIVE_RUNTIME_PACKAGES = ["sharp"];
723
723
  var DEFAULT_BACKEND_RUNTIME_PACKAGES = ["croner"];
724
+ var MOBILE_RUNTIME_PACKAGES = ["firebase"];
724
725
  var DATABASE_MODE_RUNTIME_PACKAGES = {
725
726
  single: [],
726
727
  multiple: ["@libsql/client", "bullmq", "ioredis", "protobufjs"],
@@ -730,6 +731,7 @@ var AKAN_RUNTIME_PACKAGES = new Set([
730
731
  ...SSR_RUNTIME_PACKAGES,
731
732
  ...NATIVE_RUNTIME_PACKAGES,
732
733
  ...DEFAULT_BACKEND_RUNTIME_PACKAGES,
734
+ ...MOBILE_RUNTIME_PACKAGES,
733
735
  ...Object.values(DATABASE_MODE_RUNTIME_PACKAGES).flat()
734
736
  ]);
735
737
  var DEFAULT_AKAN_IMAGE_CONFIG = {
@@ -1013,11 +1015,20 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
1013
1015
  return [...DATABASE_MODE_RUNTIME_PACKAGES[databaseMode]];
1014
1016
  }
1015
1017
  getMissingDatabaseModeDependencySpecs(databaseMode = this.defaultDatabaseMode) {
1018
+ return this.#getMissingDependencySpecs(this.getDatabaseModeRuntimePackages(databaseMode));
1019
+ }
1020
+ getMobileRuntimePackages() {
1021
+ return [...MOBILE_RUNTIME_PACKAGES];
1022
+ }
1023
+ getMissingMobileDependencySpecs() {
1024
+ return this.#getMissingDependencySpecs(this.getMobileRuntimePackages());
1025
+ }
1026
+ #getMissingDependencySpecs(libs) {
1016
1027
  const rootDependencies = {
1017
1028
  ...this.rootPackageJson.dependencies,
1018
1029
  ...this.rootPackageJson.devDependencies
1019
1030
  };
1020
- return this.getDatabaseModeRuntimePackages(databaseMode).filter((lib) => !rootDependencies[lib]).map((lib) => {
1031
+ return libs.filter((lib) => !rootDependencies[lib]).map((lib) => {
1021
1032
  const version = this.#resolveProductionDependencyVersion(lib);
1022
1033
  if (!version)
1023
1034
  throw new Error(`Dependency ${lib} not found in package.json`);
@@ -11490,6 +11501,8 @@ import path28 from "path";
11490
11501
  var BARREL_FACETS2 = new Set(["common", "srvkit", "ui", "webkit"]);
11491
11502
  var FACET_SOURCE_FILE_RE = /\.(ts|tsx)$/;
11492
11503
  var FACET_EXCLUDED_FILE_RE = /(^index\.tsx?$|\.d\.ts$|\.(test|spec)\.(ts|tsx)$|\.css$|\.scss$|\.sass$)/;
11504
+ var FACET_PASCAL_CASE_RE = /^[A-Z][A-Za-z0-9]*$/;
11505
+ var FACET_CAMEL_CASE_RE = /^[a-z][A-Za-z0-9]*$/;
11493
11506
  var MODULE_UI_TYPES = ["Template", "Unit", "Util", "View", "Zone"];
11494
11507
  var SERVICE_UI_TYPES = ["Util", "Zone"];
11495
11508
  var SCALAR_UI_TYPES = ["Template", "Unit"];
@@ -11586,18 +11599,20 @@ class DevGeneratedIndexSync {
11586
11599
  return this.#moduleContent(path28.dirname(indexPath));
11587
11600
  }
11588
11601
  async#facetContent(dir) {
11602
+ const nameCasePattern = path28.basename(dir) === "ui" ? FACET_PASCAL_CASE_RE : FACET_CAMEL_CASE_RE;
11589
11603
  const entries = await readdir2(dir, { withFileTypes: true }).catch(() => []);
11590
11604
  const exportNames = entries.flatMap((entry) => {
11591
11605
  const name = entry.name;
11592
11606
  if (name.startsWith("."))
11593
11607
  return [];
11594
11608
  if (entry.isDirectory())
11595
- return [name];
11609
+ return nameCasePattern.test(name) ? [name] : [];
11596
11610
  if (!entry.isFile())
11597
11611
  return [];
11598
11612
  if (!FACET_SOURCE_FILE_RE.test(name) || FACET_EXCLUDED_FILE_RE.test(name))
11599
11613
  return [];
11600
- return [name.replace(FACET_SOURCE_FILE_RE, "")];
11614
+ const exportName = name.replace(FACET_SOURCE_FILE_RE, "");
11615
+ return nameCasePattern.test(exportName) ? [exportName] : [];
11601
11616
  }).sort();
11602
11617
  if (exportNames.length === 0)
11603
11618
  return null;
package/index.js CHANGED
@@ -719,6 +719,7 @@ var WORKSPACE_BARREL_FACETS = ["ui", "webkit", "common", "client", "server"];
719
719
  var SSR_RUNTIME_PACKAGES = ["react", "react-dom", "react-server-dom-webpack"];
720
720
  var NATIVE_RUNTIME_PACKAGES = ["sharp"];
721
721
  var DEFAULT_BACKEND_RUNTIME_PACKAGES = ["croner"];
722
+ var MOBILE_RUNTIME_PACKAGES = ["firebase"];
722
723
  var DATABASE_MODE_RUNTIME_PACKAGES = {
723
724
  single: [],
724
725
  multiple: ["@libsql/client", "bullmq", "ioredis", "protobufjs"],
@@ -728,6 +729,7 @@ var AKAN_RUNTIME_PACKAGES = new Set([
728
729
  ...SSR_RUNTIME_PACKAGES,
729
730
  ...NATIVE_RUNTIME_PACKAGES,
730
731
  ...DEFAULT_BACKEND_RUNTIME_PACKAGES,
732
+ ...MOBILE_RUNTIME_PACKAGES,
731
733
  ...Object.values(DATABASE_MODE_RUNTIME_PACKAGES).flat()
732
734
  ]);
733
735
  var DEFAULT_AKAN_IMAGE_CONFIG = {
@@ -1011,11 +1013,20 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
1011
1013
  return [...DATABASE_MODE_RUNTIME_PACKAGES[databaseMode]];
1012
1014
  }
1013
1015
  getMissingDatabaseModeDependencySpecs(databaseMode = this.defaultDatabaseMode) {
1016
+ return this.#getMissingDependencySpecs(this.getDatabaseModeRuntimePackages(databaseMode));
1017
+ }
1018
+ getMobileRuntimePackages() {
1019
+ return [...MOBILE_RUNTIME_PACKAGES];
1020
+ }
1021
+ getMissingMobileDependencySpecs() {
1022
+ return this.#getMissingDependencySpecs(this.getMobileRuntimePackages());
1023
+ }
1024
+ #getMissingDependencySpecs(libs) {
1014
1025
  const rootDependencies = {
1015
1026
  ...this.rootPackageJson.dependencies,
1016
1027
  ...this.rootPackageJson.devDependencies
1017
1028
  };
1018
- return this.getDatabaseModeRuntimePackages(databaseMode).filter((lib) => !rootDependencies[lib]).map((lib) => {
1029
+ return libs.filter((lib) => !rootDependencies[lib]).map((lib) => {
1019
1030
  const version = this.#resolveProductionDependencyVersion(lib);
1020
1031
  if (!version)
1021
1032
  throw new Error(`Dependency ${lib} not found in package.json`);
@@ -11488,6 +11499,8 @@ import path28 from "path";
11488
11499
  var BARREL_FACETS2 = new Set(["common", "srvkit", "ui", "webkit"]);
11489
11500
  var FACET_SOURCE_FILE_RE = /\.(ts|tsx)$/;
11490
11501
  var FACET_EXCLUDED_FILE_RE = /(^index\.tsx?$|\.d\.ts$|\.(test|spec)\.(ts|tsx)$|\.css$|\.scss$|\.sass$)/;
11502
+ var FACET_PASCAL_CASE_RE = /^[A-Z][A-Za-z0-9]*$/;
11503
+ var FACET_CAMEL_CASE_RE = /^[a-z][A-Za-z0-9]*$/;
11491
11504
  var MODULE_UI_TYPES = ["Template", "Unit", "Util", "View", "Zone"];
11492
11505
  var SERVICE_UI_TYPES = ["Util", "Zone"];
11493
11506
  var SCALAR_UI_TYPES = ["Template", "Unit"];
@@ -11584,18 +11597,20 @@ class DevGeneratedIndexSync {
11584
11597
  return this.#moduleContent(path28.dirname(indexPath));
11585
11598
  }
11586
11599
  async#facetContent(dir) {
11600
+ const nameCasePattern = path28.basename(dir) === "ui" ? FACET_PASCAL_CASE_RE : FACET_CAMEL_CASE_RE;
11587
11601
  const entries = await readdir2(dir, { withFileTypes: true }).catch(() => []);
11588
11602
  const exportNames = entries.flatMap((entry) => {
11589
11603
  const name = entry.name;
11590
11604
  if (name.startsWith("."))
11591
11605
  return [];
11592
11606
  if (entry.isDirectory())
11593
- return [name];
11607
+ return nameCasePattern.test(name) ? [name] : [];
11594
11608
  if (!entry.isFile())
11595
11609
  return [];
11596
11610
  if (!FACET_SOURCE_FILE_RE.test(name) || FACET_EXCLUDED_FILE_RE.test(name))
11597
11611
  return [];
11598
- return [name.replace(FACET_SOURCE_FILE_RE, "")];
11612
+ const exportName = name.replace(FACET_SOURCE_FILE_RE, "");
11613
+ return nameCasePattern.test(exportName) ? [exportName] : [];
11599
11614
  }).sort();
11600
11615
  if (exportNames.length === 0)
11601
11616
  return null;
@@ -17161,6 +17176,31 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
17161
17176
  throw error;
17162
17177
  }
17163
17178
  }
17179
+ async confirmMobileDependencyInstall(installSpecs) {
17180
+ return await confirm3({
17181
+ message: [`Mobile builds require missing dependencies: ${installSpecs.join(", ")}.`, "Install them now?"].join(" "),
17182
+ default: true
17183
+ });
17184
+ }
17185
+ async syncMobileDependencies(app, akanConfig3) {
17186
+ const installSpecs = akanConfig3.getMissingMobileDependencySpecs();
17187
+ if (installSpecs.length === 0)
17188
+ return;
17189
+ const shouldInstall = await this.confirmMobileDependencyInstall(installSpecs);
17190
+ if (!shouldInstall)
17191
+ throw new Error(`Mobile builds require missing dependencies: ${installSpecs.join(", ")}.`);
17192
+ const spinner2 = app.workspace.spinning("Installing mobile dependencies...");
17193
+ try {
17194
+ await app.workspace.spawn("bun", ["add", ...installSpecs], {
17195
+ stdio: "inherit"
17196
+ });
17197
+ await app.workspace.getPackageJson({ refresh: true });
17198
+ spinner2.succeed("Installed mobile dependencies");
17199
+ } catch (error) {
17200
+ spinner2.fail("Failed to install mobile dependencies");
17201
+ throw error;
17202
+ }
17203
+ }
17164
17204
  async createApplication(appName, workspace, { start = false, libs = [] } = {}) {
17165
17205
  const spinner2 = workspace.spinning("Creating application...");
17166
17206
  const app = await this.applicationRunner.createApplication(appName, workspace, libs);
@@ -17282,6 +17322,7 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
17282
17322
  noAllowProvisioningUpdates = false
17283
17323
  } = {}) {
17284
17324
  await app.scanSync({ write });
17325
+ await this.syncMobileDependencies(app, await app.getConfig());
17285
17326
  await this.applicationRunner.startIos(app, {
17286
17327
  open,
17287
17328
  operation,
@@ -17310,6 +17351,7 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
17310
17351
  regenerate = false
17311
17352
  } = {}) {
17312
17353
  await app.scanSync({ write });
17354
+ await this.syncMobileDependencies(app, await app.getConfig());
17313
17355
  await this.applicationRunner.startAndroid(app, {
17314
17356
  open,
17315
17357
  operation,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akanjs/cli",
3
- "version": "2.3.11",
3
+ "version": "2.3.12-rc.0",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -34,7 +34,7 @@
34
34
  "@langchain/openai": "^1.4.6",
35
35
  "@tailwindcss/node": "^4.3.0",
36
36
  "@trapezedev/project": "^7.1.4",
37
- "akanjs": "2.3.11",
37
+ "akanjs": "2.3.12-rc.0",
38
38
  "chalk": "^5.6.2",
39
39
  "commander": "^14.0.3",
40
40
  "daisyui": "5.5.23",
@@ -12,18 +12,24 @@ interface Options {
12
12
 
13
13
  const sourceFilePattern = /\.(ts|tsx)$/;
14
14
  const excludedFilePattern = /(^index\.tsx?$|\.d\.ts$|\.(test|spec)\.(ts|tsx)$|\.css$|\.scss$|\.sass$)/;
15
+ // `ui` exports PascalCase names only; `common`/`srvkit`/`webkit` export camelCase names only. Names with
16
+ // dots, underscores, or hyphens (e.g. `foo.helper`, `Globe_Dynamic`, `kebab-case`) match neither and are skipped.
17
+ const pascalCasePattern = /^[A-Z][A-Za-z0-9]*$/;
18
+ const camelCasePattern = /^[a-z][A-Za-z0-9]*$/;
19
+ const nameCasePatternForFacet = (facet: string) => (facet === "ui" ? pascalCasePattern : camelCasePattern);
15
20
 
16
21
  export default async function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dict = {}, options: Options = {}) {
17
22
  const { exec, facet } = options;
18
23
  if (!exec || !facet || !(await exec.exists(facet))) return null;
19
24
 
25
+ const nameCasePattern = nameCasePatternForFacet(facet);
20
26
  const { files, dirs } = await exec.getFilesAndDirs(facet);
21
27
  const exportNames = [
22
28
  ...files
23
29
  .filter((filename) => sourceFilePattern.test(filename) && !excludedFilePattern.test(filename))
24
30
  .map((filename) => filename.replace(sourceFilePattern, "")),
25
31
  ...dirs.filter((dirname) => !dirname.startsWith(".")),
26
- ].sort();
32
+ ].filter((name) => nameCasePattern.test(name)).sort();
27
33
 
28
34
  if (exportNames.length === 0) return null;
29
35
  return {
@@ -9,5 +9,5 @@ alwaysApply: false
9
9
  - Use Bun and ESM assumptions from the root `tsconfig.json`.
10
10
  - Prefer path aliases over deep relative imports when crossing package boundaries.
11
11
  - Use `akanjs/*` for framework facets, `@apps/*` for apps, `@libs/*` for shared libs, and `@contract/*` for contract code.
12
- - Respect existing client/server entrypoints such as `@libs/shared/client`, `@libs/shared/server`, `@apps/<% appName %>/client`, and `@apps/<% appName %>/server`.
12
+ - Respect existing client/server entrypoints such as `@libs/shared/client`, `@libs/shared/server`, `@apps/myapp/client`, and `@apps/myapp/server`.
13
13
  - Let Biome organize imports instead of manually reshuffling unrelated imports.