@stacksolo/cli 0.1.3 → 0.1.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.
package/dist/index.js CHANGED
@@ -877,25 +877,28 @@ async function scaffoldTemplates(cwd, projectType, uiFramework) {
877
877
  }
878
878
  async function scaffoldFunctionApi(cwd, name) {
879
879
  const dir = path.join(cwd, name);
880
- await fs.mkdir(dir, { recursive: true });
880
+ const srcDir = path.join(dir, "src");
881
+ await fs.mkdir(srcDir, { recursive: true });
881
882
  await fs.writeFile(
882
883
  path.join(dir, "package.json"),
883
884
  JSON.stringify(
884
885
  {
885
886
  name,
886
887
  version: "1.0.0",
887
- main: "index.js",
888
+ main: "dist/index.js",
888
889
  type: "module",
889
890
  scripts: {
890
- start: "npx functions-framework --target=handler",
891
- build: "tsc"
891
+ dev: "tsup src/index.ts --format esm --target node20 --watch --onSuccess 'functions-framework --source=dist --target=handler'",
892
+ build: "tsup src/index.ts --format esm --target node20",
893
+ start: "functions-framework --source=dist --target=handler"
892
894
  },
893
895
  dependencies: {
894
896
  "@google-cloud/functions-framework": "^3.3.0"
895
897
  },
896
898
  devDependencies: {
897
899
  typescript: "^5.0.0",
898
- "@types/node": "^20.0.0"
900
+ "@types/node": "^20.0.0",
901
+ tsup: "^8.0.0"
899
902
  }
900
903
  },
901
904
  null,
@@ -903,15 +906,21 @@ async function scaffoldFunctionApi(cwd, name) {
903
906
  ) + "\n"
904
907
  );
905
908
  await fs.writeFile(
906
- path.join(dir, "index.ts"),
907
- `import functions from '@google-cloud/functions-framework';
909
+ path.join(srcDir, "index.ts"),
910
+ `/**
911
+ * ${name} HTTP function
912
+ * Generated by: stacksolo init
913
+ */
914
+
915
+ import type { Request, Response } from '@google-cloud/functions-framework';
908
916
 
909
- functions.http('handler', (req, res) => {
917
+ export function handler(req: Request, res: Response): void {
910
918
  const { method, path } = req;
911
919
 
912
920
  // Health check
913
921
  if (path === '/health') {
914
- return res.json({ status: 'ok' });
922
+ res.json({ status: 'ok' });
923
+ return;
915
924
  }
916
925
 
917
926
  // Your API routes here
@@ -920,7 +929,7 @@ functions.http('handler', (req, res) => {
920
929
  method,
921
930
  path,
922
931
  });
923
- });
932
+ }
924
933
  `
925
934
  );
926
935
  await fs.writeFile(
@@ -930,19 +939,31 @@ functions.http('handler', (req, res) => {
930
939
  compilerOptions: {
931
940
  target: "ES2022",
932
941
  module: "ESNext",
933
- moduleResolution: "node",
942
+ moduleResolution: "bundler",
934
943
  esModuleInterop: true,
935
944
  strict: true,
945
+ skipLibCheck: true,
936
946
  outDir: "dist",
947
+ rootDir: "src",
937
948
  declaration: true
938
949
  },
939
- include: ["*.ts"]
950
+ include: ["src/**/*"],
951
+ exclude: ["node_modules", "dist"]
940
952
  },
941
953
  null,
942
954
  2
943
955
  ) + "\n"
944
956
  );
945
- return [`${name}/package.json`, `${name}/index.ts`, `${name}/tsconfig.json`];
957
+ await fs.writeFile(
958
+ path.join(dir, ".gitignore"),
959
+ `node_modules/
960
+ dist/
961
+ *.log
962
+ .env
963
+ .env.local
964
+ `
965
+ );
966
+ return [`${name}/package.json`, `${name}/src/index.ts`, `${name}/tsconfig.json`, `${name}/.gitignore`];
946
967
  }
947
968
  async function scaffoldContainerApi(cwd, name) {
948
969
  const dir = path.join(cwd, name);
@@ -1027,25 +1048,28 @@ CMD ["node", "index.js"]
1027
1048
  }
1028
1049
  async function scaffoldFunctionCron(cwd, name) {
1029
1050
  const dir = path.join(cwd, name);
1030
- await fs.mkdir(dir, { recursive: true });
1051
+ const srcDir = path.join(dir, "src");
1052
+ await fs.mkdir(srcDir, { recursive: true });
1031
1053
  await fs.writeFile(
1032
1054
  path.join(dir, "package.json"),
1033
1055
  JSON.stringify(
1034
1056
  {
1035
1057
  name,
1036
1058
  version: "1.0.0",
1037
- main: "index.js",
1059
+ main: "dist/index.js",
1038
1060
  type: "module",
1039
1061
  scripts: {
1040
- start: "npx functions-framework --target=handler",
1041
- build: "tsc"
1062
+ dev: "tsup src/index.ts --format esm --target node20 --watch --onSuccess 'functions-framework --source=dist --target=handler'",
1063
+ build: "tsup src/index.ts --format esm --target node20",
1064
+ start: "functions-framework --source=dist --target=handler"
1042
1065
  },
1043
1066
  dependencies: {
1044
1067
  "@google-cloud/functions-framework": "^3.3.0"
1045
1068
  },
1046
1069
  devDependencies: {
1047
1070
  typescript: "^5.0.0",
1048
- "@types/node": "^20.0.0"
1071
+ "@types/node": "^20.0.0",
1072
+ tsup: "^8.0.0"
1049
1073
  }
1050
1074
  },
1051
1075
  null,
@@ -1053,17 +1077,22 @@ async function scaffoldFunctionCron(cwd, name) {
1053
1077
  ) + "\n"
1054
1078
  );
1055
1079
  await fs.writeFile(
1056
- path.join(dir, "index.ts"),
1057
- `import functions from '@google-cloud/functions-framework';
1080
+ path.join(srcDir, "index.ts"),
1081
+ `/**
1082
+ * ${name} Cron function
1083
+ * Generated by: stacksolo init
1084
+ */
1085
+
1086
+ import type { Request, Response } from '@google-cloud/functions-framework';
1058
1087
 
1059
- functions.http('handler', (req, res) => {
1088
+ export function handler(req: Request, res: Response): void {
1060
1089
  console.log('Cron triggered:', new Date().toISOString());
1061
1090
 
1062
1091
  // Your scheduled job logic here
1063
1092
 
1064
1093
  console.log('Job complete');
1065
1094
  res.json({ status: 'ok' });
1066
- });
1095
+ }
1067
1096
  `
1068
1097
  );
1069
1098
  await fs.writeFile(
@@ -1073,19 +1102,31 @@ functions.http('handler', (req, res) => {
1073
1102
  compilerOptions: {
1074
1103
  target: "ES2022",
1075
1104
  module: "ESNext",
1076
- moduleResolution: "node",
1105
+ moduleResolution: "bundler",
1077
1106
  esModuleInterop: true,
1078
1107
  strict: true,
1108
+ skipLibCheck: true,
1079
1109
  outDir: "dist",
1110
+ rootDir: "src",
1080
1111
  declaration: true
1081
1112
  },
1082
- include: ["*.ts"]
1113
+ include: ["src/**/*"],
1114
+ exclude: ["node_modules", "dist"]
1083
1115
  },
1084
1116
  null,
1085
1117
  2
1086
1118
  ) + "\n"
1087
1119
  );
1088
- return [`${name}/package.json`, `${name}/index.ts`, `${name}/tsconfig.json`];
1120
+ await fs.writeFile(
1121
+ path.join(dir, ".gitignore"),
1122
+ `node_modules/
1123
+ dist/
1124
+ *.log
1125
+ .env
1126
+ .env.local
1127
+ `
1128
+ );
1129
+ return [`${name}/package.json`, `${name}/src/index.ts`, `${name}/tsconfig.json`, `${name}/.gitignore`];
1089
1130
  }
1090
1131
  async function scaffoldStaticWeb(cwd, name) {
1091
1132
  const dir = path.join(cwd, name);
@@ -15552,6 +15593,9 @@ function generateConfigMap(options) {
15552
15593
  } else {
15553
15594
  data.PUBSUB_EMULATOR_HOST = "pubsub-emulator:8085";
15554
15595
  }
15596
+ if (options.kernelUrl) {
15597
+ data.KERNEL_URL = options.kernelUrl;
15598
+ }
15555
15599
  if (options.additionalEnv) {
15556
15600
  Object.assign(data, options.additionalEnv);
15557
15601
  }
@@ -15660,9 +15704,31 @@ function generateFunctionManifests(options) {
15660
15704
  const functionName = sanitizeName(options.function.name);
15661
15705
  const runtimeConfig = getRuntimeConfig(options.function.runtime, options.function.entryPoint);
15662
15706
  const isPython = isPythonRuntime(options.function.runtime);
15663
- const installCmd = isPython ? "pip install -r requirements.txt 2>/dev/null || true && pip install functions-framework" : "npm install";
15664
15707
  const runCmd = isPython ? runtimeConfig.command.join(" ") : "npm run dev 2>/dev/null || " + runtimeConfig.command.join(" ");
15665
- const containerCommand = ["sh", "-c", `${installCmd} && ${runCmd}`];
15708
+ const containerCommand = isPython ? [
15709
+ "sh",
15710
+ "-c",
15711
+ [
15712
+ "cp -r /source/* /app/ 2>/dev/null || true",
15713
+ "cd /app",
15714
+ "pip install -r requirements.txt 2>/dev/null || true",
15715
+ "pip install functions-framework",
15716
+ runCmd
15717
+ ].join(" && ")
15718
+ ] : [
15719
+ "sh",
15720
+ "-c",
15721
+ [
15722
+ // Copy source files to /app, excluding node_modules (macOS binaries don't work in Linux)
15723
+ "cd /source",
15724
+ "find . -maxdepth 1 ! -name node_modules ! -name . -exec cp -r {} /app/ \\;",
15725
+ "cd /app",
15726
+ // Always install fresh for Linux platform
15727
+ "npm install",
15728
+ // Run the dev server
15729
+ runCmd
15730
+ ].join(" && ")
15731
+ ];
15666
15732
  const labels = {
15667
15733
  "app.kubernetes.io/name": functionName,
15668
15734
  "app.kubernetes.io/component": "function",
@@ -15711,16 +15777,21 @@ function generateFunctionManifests(options) {
15711
15777
  volumeMounts: [
15712
15778
  {
15713
15779
  name: "source",
15780
+ mountPath: "/source",
15781
+ readOnly: true
15782
+ },
15783
+ {
15784
+ name: "workdir",
15714
15785
  mountPath: "/app"
15715
15786
  }
15716
15787
  ],
15717
15788
  workingDir: "/app",
15718
15789
  resources: {
15719
15790
  limits: {
15720
- memory: options.function.memory || "256Mi"
15791
+ memory: options.function.memory || "512Mi"
15721
15792
  },
15722
15793
  requests: {
15723
- memory: "128Mi"
15794
+ memory: "256Mi"
15724
15795
  }
15725
15796
  }
15726
15797
  }
@@ -15732,6 +15803,10 @@ function generateFunctionManifests(options) {
15732
15803
  path: options.sourceDir,
15733
15804
  type: "DirectoryOrCreate"
15734
15805
  }
15806
+ },
15807
+ {
15808
+ name: "workdir",
15809
+ emptyDir: {}
15735
15810
  }
15736
15811
  ]
15737
15812
  }
@@ -15817,7 +15892,21 @@ function generateUIManifests(options) {
15817
15892
  {
15818
15893
  name: uiName,
15819
15894
  image: "node:20-slim",
15820
- command: ["sh", "-c", `npm install && ${frameworkConfig.command.join(" ")}`],
15895
+ // Copy source (excluding node_modules) to working directory, then install fresh Linux deps
15896
+ command: [
15897
+ "sh",
15898
+ "-c",
15899
+ [
15900
+ // Copy source files to /app, excluding node_modules (macOS binaries don't work in Linux)
15901
+ "cd /source",
15902
+ "find . -maxdepth 1 ! -name node_modules ! -name . -exec cp -r {} /app/ \\;",
15903
+ "cd /app",
15904
+ // Always install fresh for Linux platform
15905
+ "npm install",
15906
+ // Run the dev server
15907
+ frameworkConfig.command.join(" ")
15908
+ ].join(" && ")
15909
+ ],
15821
15910
  ports: [
15822
15911
  {
15823
15912
  containerPort: options.port,
@@ -15832,16 +15921,21 @@ function generateUIManifests(options) {
15832
15921
  volumeMounts: [
15833
15922
  {
15834
15923
  name: "source",
15924
+ mountPath: "/source",
15925
+ readOnly: true
15926
+ },
15927
+ {
15928
+ name: "workdir",
15835
15929
  mountPath: "/app"
15836
15930
  }
15837
15931
  ],
15838
15932
  workingDir: "/app",
15839
15933
  resources: {
15840
15934
  limits: {
15841
- memory: "512Mi"
15935
+ memory: "1Gi"
15842
15936
  },
15843
15937
  requests: {
15844
- memory: "256Mi"
15938
+ memory: "512Mi"
15845
15939
  }
15846
15940
  }
15847
15941
  }
@@ -15853,6 +15947,10 @@ function generateUIManifests(options) {
15853
15947
  path: options.sourceDir,
15854
15948
  type: "DirectoryOrCreate"
15855
15949
  }
15950
+ },
15951
+ {
15952
+ name: "workdir",
15953
+ emptyDir: {}
15856
15954
  }
15857
15955
  ]
15858
15956
  }
@@ -16569,7 +16667,7 @@ function generateNginxConfig(routes, servicePortMap) {
16569
16667
  const basePath = route.path.replace(/\/?\*$/, "");
16570
16668
  return `
16571
16669
  location ${basePath}/ {
16572
- proxy_pass ${upstream}/;
16670
+ proxy_pass ${upstream};
16573
16671
  proxy_http_version 1.1;
16574
16672
  proxy_set_header Upgrade $http_upgrade;
16575
16673
  proxy_set_header Connection 'upgrade';
@@ -16687,8 +16785,14 @@ function generateK8sManifests(options) {
16687
16785
  const projectName = config.project.name;
16688
16786
  const portAllocator = createPortAllocator();
16689
16787
  const servicePortMap = {};
16788
+ let kernelUrl;
16789
+ if (config.project.kernel) {
16790
+ kernelUrl = `http://${config.project.kernel.name}:8090`;
16791
+ } else if (config.project.gcpKernel) {
16792
+ kernelUrl = `http://${config.project.gcpKernel.name}:8080`;
16793
+ }
16690
16794
  manifests.push(generateNamespace(projectName));
16691
- manifests.push(generateConfigMap({ projectName }));
16795
+ manifests.push(generateConfigMap({ projectName, kernelUrl }));
16692
16796
  if (includeEmulators) {
16693
16797
  manifests.push(generateFirebaseEmulator({ projectName }));
16694
16798
  manifests.push(generatePubSubEmulator({ projectName }));