@tremho/mist-lift 1.0.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.
Files changed (121) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +37 -0
  3. package/build/commands/actions/initQuestions.js +136 -0
  4. package/build/commands/actions/initQuestions.js.map +1 -0
  5. package/build/commands/actions/makePackageJson.js +2 -0
  6. package/build/commands/actions/makePackageJson.js.map +1 -0
  7. package/build/commands/actions/setupPackageJson.js +68 -0
  8. package/build/commands/actions/setupPackageJson.js.map +1 -0
  9. package/build/commands/build.js +191 -0
  10. package/build/commands/build.js.map +1 -0
  11. package/build/commands/builtin/ApiDocMaker.js +72 -0
  12. package/build/commands/builtin/ApiDocMaker.js.map +1 -0
  13. package/build/commands/builtin/BuiltInHandler.js +62 -0
  14. package/build/commands/builtin/BuiltInHandler.js.map +1 -0
  15. package/build/commands/builtin/DeployBuiltInZip.js +57 -0
  16. package/build/commands/builtin/DeployBuiltInZip.js.map +1 -0
  17. package/build/commands/builtin/StageWebrootZip.js +70 -0
  18. package/build/commands/builtin/StageWebrootZip.js.map +1 -0
  19. package/build/commands/builtin/prebuilt-zips/API.zip +0 -0
  20. package/build/commands/builtin/prebuilt-zips/FileServe.zip +0 -0
  21. package/build/commands/builtin/prebuilt-zips/Webroot.zip +0 -0
  22. package/build/commands/create.js +75 -0
  23. package/build/commands/create.js.map +1 -0
  24. package/build/commands/deploy.js +187 -0
  25. package/build/commands/deploy.js.map +1 -0
  26. package/build/commands/doctor.js +137 -0
  27. package/build/commands/doctor.js.map +1 -0
  28. package/build/commands/help.js +205 -0
  29. package/build/commands/help.js.map +1 -0
  30. package/build/commands/init.js +92 -0
  31. package/build/commands/init.js.map +1 -0
  32. package/build/commands/package.js +249 -0
  33. package/build/commands/package.js.map +1 -0
  34. package/build/commands/publish.js +344 -0
  35. package/build/commands/publish.js.map +1 -0
  36. package/build/commands/settings.js +95 -0
  37. package/build/commands/settings.js.map +1 -0
  38. package/build/commands/start.js +66 -0
  39. package/build/commands/start.js.map +1 -0
  40. package/build/commands/test.js +52 -0
  41. package/build/commands/test.js.map +1 -0
  42. package/build/commands/user.js +20 -0
  43. package/build/commands/user.js.map +1 -0
  44. package/build/expressRoutes/all.js +129 -0
  45. package/build/expressRoutes/all.js.map +1 -0
  46. package/build/expressRoutes/api.js +22 -0
  47. package/build/expressRoutes/api.js.map +1 -0
  48. package/build/expressRoutes/functionBinder.js +191 -0
  49. package/build/expressRoutes/functionBinder.js.map +1 -0
  50. package/build/lib/CaseUtils.js +57 -0
  51. package/build/lib/CaseUtils.js.map +1 -0
  52. package/build/lib/DirectoryUtils.js +37 -0
  53. package/build/lib/DirectoryUtils.js.map +1 -0
  54. package/build/lib/LiftConfig.js +83 -0
  55. package/build/lib/LiftConfig.js.map +1 -0
  56. package/build/lib/LiftVersion.js +117 -0
  57. package/build/lib/LiftVersion.js.map +1 -0
  58. package/build/lib/Tests/fileCompare.test.js +55 -0
  59. package/build/lib/Tests/fileCompare.test.js.map +1 -0
  60. package/build/lib/askQuestion.js +41 -0
  61. package/build/lib/askQuestion.js.map +1 -0
  62. package/build/lib/executeCommand.js +45 -0
  63. package/build/lib/executeCommand.js.map +1 -0
  64. package/build/lib/fileCompare.js +48 -0
  65. package/build/lib/fileCompare.js.map +1 -0
  66. package/build/lib/openAPI/ApiBuildCollector.js +58 -0
  67. package/build/lib/openAPI/ApiBuildCollector.js.map +1 -0
  68. package/build/lib/openAPI/WebrootFileSupport.js +44 -0
  69. package/build/lib/openAPI/WebrootFileSupport.js.map +1 -0
  70. package/build/lib/openAPI/openApiConstruction.js +203 -0
  71. package/build/lib/openAPI/openApiConstruction.js.map +1 -0
  72. package/build/lib/pathResolve.js +27 -0
  73. package/build/lib/pathResolve.js.map +1 -0
  74. package/build/lib/utils.js +76 -0
  75. package/build/lib/utils.js.map +1 -0
  76. package/build/lift.js +124 -0
  77. package/build/lift.js.map +1 -0
  78. package/package.json +69 -0
  79. package/src/commands/actions/initQuestions.ts +131 -0
  80. package/src/commands/actions/makePackageJson.ts +0 -0
  81. package/src/commands/actions/setupPackageJson.ts +36 -0
  82. package/src/commands/build.ts +165 -0
  83. package/src/commands/builtin/ApiDocMaker.ts +70 -0
  84. package/src/commands/builtin/BuiltInHandler.ts +51 -0
  85. package/src/commands/builtin/DeployBuiltInZip.ts +27 -0
  86. package/src/commands/builtin/StageWebrootZip.ts +39 -0
  87. package/src/commands/builtin/prebuilt-zips/API.zip +0 -0
  88. package/src/commands/builtin/prebuilt-zips/FileServe.zip +0 -0
  89. package/src/commands/builtin/prebuilt-zips/Webroot.zip +0 -0
  90. package/src/commands/create.ts +55 -0
  91. package/src/commands/deploy.ts +171 -0
  92. package/src/commands/doctor.ts +102 -0
  93. package/src/commands/help.ts +171 -0
  94. package/src/commands/init.ts +62 -0
  95. package/src/commands/package.ts +228 -0
  96. package/src/commands/publish.ts +350 -0
  97. package/src/commands/settings.ts +76 -0
  98. package/src/commands/start.ts +46 -0
  99. package/src/commands/test.ts +38 -0
  100. package/src/commands/user.ts +20 -0
  101. package/src/expressRoutes/all.ts +104 -0
  102. package/src/expressRoutes/api.ts +24 -0
  103. package/src/expressRoutes/functionBinder.ts +169 -0
  104. package/src/lib/CaseUtils.ts +68 -0
  105. package/src/lib/DirectoryUtils.ts +36 -0
  106. package/src/lib/LiftConfig.ts +83 -0
  107. package/src/lib/LiftVersion.ts +95 -0
  108. package/src/lib/Tests/dir1/file.1 +0 -0
  109. package/src/lib/Tests/dir1/file.2 +0 -0
  110. package/src/lib/Tests/dir2/file.1 +0 -0
  111. package/src/lib/Tests/fileCompare.test.ts +34 -0
  112. package/src/lib/askQuestion.ts +18 -0
  113. package/src/lib/executeCommand.ts +38 -0
  114. package/src/lib/fileCompare.ts +46 -0
  115. package/src/lib/openAPI/ApiBuildCollector.ts +47 -0
  116. package/src/lib/openAPI/WebrootFileSupport.ts +21 -0
  117. package/src/lib/openAPI/openApiConstruction.ts +202 -0
  118. package/src/lib/pathResolve.ts +32 -0
  119. package/src/lib/utils.ts +45 -0
  120. package/src/lift.ts +82 -0
  121. package/tsconfig.json +28 -0
@@ -0,0 +1,169 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+
4
+ const clearModule = require('clear-module')
5
+
6
+ import * as ac from "ansi-colors"
7
+ import {resolvePaths} from "../lib/pathResolve";
8
+ import {GetWebrootServePaths} from "../lib/openAPI/WebrootFileSupport"
9
+ import {doBuildAsync} from "../commands/build"
10
+
11
+
12
+ import express from 'express'
13
+ import {gatherFunctionDefinitions} from "../lib/openAPI/ApiBuildCollector";
14
+ import {buildOpenApi} from "../lib/openAPI/openApiConstruction";
15
+ const router = express.Router();
16
+
17
+
18
+ export function functionBinder() {
19
+ const defs = gatherFunctionDefinitions();
20
+ buildOpenApi(defs).then(() => { // creates apidoc.yaml for when /api is run
21
+
22
+
23
+ const projectPaths = resolvePaths();
24
+
25
+ for (let def of defs) {
26
+ const {name, pathMap, allowedMethods} = def;
27
+ const methods = allowedMethods.split(',');
28
+ for (let method of methods) {
29
+ method = method.trim().toLowerCase();
30
+ let rpath = path.join(projectPaths.buildPath, 'functions', name, 'src', 'main.js')
31
+ clearModule(rpath);
32
+ const {start} = require(rpath);
33
+
34
+ let entryRoot = pathMap;
35
+ let n = entryRoot.indexOf("/{");
36
+ if (n !== -1) entryRoot = entryRoot.substring(0, n) + "/*";
37
+
38
+ const callNoBody = (pathMap: string, req: any, res: any) => {
39
+ const event = requestToEvent(pathMap, req)
40
+ Promise.resolve(start(event, null, null)).then(respOut => {
41
+ handleResponse(res, respOut);
42
+ })
43
+ }
44
+ const callWithBody = (pathMap: string, req: any, res: any) => {
45
+ const event = requestToEvent(pathMap, req)
46
+ event.body = req.body
47
+ Promise.resolve(start(event, null, null)).then(respOut => {
48
+ handleResponse(res, respOut);
49
+ })
50
+ }
51
+
52
+ if (method === 'get') {
53
+ router.get(entryRoot, (req, res) => callNoBody(pathMap, req, res))
54
+ } else if (method === 'post') {
55
+ router.get(entryRoot, (req, res) => callWithBody(pathMap, req, res))
56
+ } else if (method === 'put') {
57
+ router.put(entryRoot, (req, res) => callWithBody(pathMap, req, res))
58
+ } else if (method === 'patch') {
59
+ router.patch(entryRoot, (req, res) => callWithBody(pathMap, req, res))
60
+ } else if (method === 'delete') {
61
+ router.delete(entryRoot, (req, res) => callNoBody(pathMap, req, res))
62
+ } else {
63
+ console.log(ac.red.bold("Cannot map method ") + ac.blue.bold(method))
64
+ }
65
+
66
+ }
67
+ }
68
+ })
69
+ }
70
+
71
+ function requestToEvent(template:string, req:any) {
72
+ let path = req.originalUrl
73
+ const qi = path.indexOf('?');
74
+ if(qi !== -1) path = path.substring(0,qi);
75
+ let ptci = path.indexOf("://")+3;
76
+ let ei = path.indexOf("/", ptci);
77
+ let host = path.substring(0, ei);
78
+ if(ptci < 3) {
79
+ host = req.headers?.origin;
80
+ if(!host) {
81
+ host = req.headers?.referer ?? "";
82
+ let ptci = path.indexOf("://")+3;
83
+ let ei = path.indexOf("/", ptci);
84
+ host = ptci > 3 ? path.substring(0, ei) : "";
85
+ }
86
+ if(!host) {
87
+ // todo: http or https?
88
+ host = "http://"+req.headers?.host ?? "";
89
+ }
90
+ }
91
+ var cookies:any = {};
92
+ var cookieString = req.headers?.cookie ?? "";
93
+ var crumbs = cookieString.split(';')
94
+ for(let c of crumbs) {
95
+ const pair:string[] = c.split('=');
96
+ if(pair.length === 2) cookies[pair[0]] = pair[1]
97
+ }
98
+ const parameters:any = {}
99
+ const tslots = template.split('/');
100
+ const pslots = path.split('/');
101
+ for(let i = 0; i< tslots.length; i++) {
102
+ const brknm = (tslots[i]??"").trim();
103
+ if(brknm.charAt(0) === '{') {
104
+ const pn = brknm.substring(1, brknm.length - 1);
105
+ parameters[pn] = (pslots[i]??"").trim();
106
+ }
107
+ }
108
+ for(let p of Object.getOwnPropertyNames(req.query)) {
109
+ parameters[p] = req.query[p]
110
+ }
111
+ const eventOut:any = {
112
+ requestContext: req,
113
+ request: {
114
+ originalUrl: host + req.originalUrl,
115
+ headers: req.headers
116
+ },
117
+ cookies,
118
+ parameters
119
+ }
120
+ return eventOut;
121
+ }
122
+ function handleResponse(res:any, resp:any)
123
+ {
124
+ // console.log(">>>>>>>> Handling response >>>>>>>>>")
125
+
126
+ if(resp) {
127
+ if (resp.cookies !== undefined) {
128
+ // console.log("--- see cookies", resp.cookies)
129
+ var cookies:any = []
130
+ if(Array.isArray(resp.cookies)) {
131
+ cookies = resp.cookies
132
+ } else {
133
+ var age = resp.cookies.expireSeconds ?? 60; // 1 minute
134
+ delete resp.expireSeconds;
135
+ Object.getOwnPropertyNames(resp.cookies).forEach(name => {
136
+ var value = resp.cookies[name];
137
+ cookies.push(`${name}=${value}; Max-Age=${age}; `);
138
+ })
139
+ }
140
+ // console.log("cookies being set", cookies)
141
+ res.setHeader("set-cookie", cookies);
142
+ delete resp.cookies;
143
+ }
144
+ if (resp.headers !== undefined) {
145
+ // if (resp.statusCode === 301) ClogTrace("Redirecting...");
146
+ for (var hdr of Object.getOwnPropertyNames(resp.headers)) {
147
+ // ClogTrace("Setting header ", hdr, resp.headers[hdr]);
148
+ res.setHeader(hdr, resp.headers[hdr])
149
+ }
150
+ delete resp.headers;
151
+ // console.log("past setting headers");
152
+ }
153
+ if (resp.statusCode !== undefined) {
154
+ res.statusCode = resp.statusCode;
155
+ delete resp.statusCode
156
+ }
157
+
158
+ if (resp.contentType !== undefined) {
159
+ res.setHeader("Content-Type", resp.contentType);
160
+ delete resp.contentType
161
+ }
162
+ if(resp.body) resp = resp.body;
163
+ }
164
+ // console.log("headers to be sent", res.getHeaders())
165
+ // console.log("-- Sending response", resp)
166
+ res.send(resp);
167
+ }
168
+
169
+ export default router
@@ -0,0 +1,68 @@
1
+
2
+ // separate a case-joined string into a string of space-separated words
3
+ export function separated(
4
+ instr:string
5
+ ):string
6
+ {
7
+ let outstr = '';
8
+ for(let i=0; i<instr.length; i++) {
9
+ let c = instr.charAt(i);
10
+ if(!isAlphaNum(c) || isUpperCase(c)) {
11
+ outstr += ' ';
12
+ }
13
+ outstr += c.toLowerCase();
14
+ }
15
+
16
+ return outstr.trim()
17
+ }
18
+
19
+ function isUpperCase(instr:string) {
20
+ return instr?.toUpperCase() === instr
21
+ }
22
+ function isAlphaNum(char:string) {
23
+ let alnum = false
24
+ const cc = char.toUpperCase().charCodeAt(0)
25
+ if(cc >= 'A'.charCodeAt(0) && cc <= 'Z'.charCodeAt(0)) alnum = true
26
+ if(cc >= '0'.charCodeAt(0) && cc <= '9'.charCodeAt(0)) alnum = true
27
+ return alnum
28
+ }
29
+
30
+ export function camelCase(
31
+ instr:string
32
+ ):string
33
+ {
34
+ const seps = separated(instr).split(' ')
35
+ for(let i=1; i<seps.length; i++) {
36
+ seps[i] = seps[i].charAt(0).toUpperCase()+seps[i].substring(1)
37
+ }
38
+ return seps.join('')
39
+ }
40
+
41
+ export function pascalCase(
42
+ instr:string
43
+ ):string
44
+ {
45
+ const seps = separated(instr).split(' ')
46
+ for(let i=0; i<seps.length; i++) {
47
+ seps[i] = seps[i].charAt(0).toUpperCase()+seps[i].substring(1)
48
+ }
49
+ return seps.join('')
50
+ }
51
+
52
+ export function snakeCase(
53
+ instr:string
54
+ ):string
55
+ {
56
+ return dashCase(instr, "_")
57
+ }
58
+ export function dashCase(
59
+ instr:string,
60
+ dashChar: string = '-',
61
+ allCaps: boolean = false
62
+ ):string
63
+ {
64
+ const seps = separated(instr).split(' ')
65
+ let out = seps.join(dashChar)
66
+ if(allCaps) out = out.toUpperCase();
67
+ return out;
68
+ }
@@ -0,0 +1,36 @@
1
+
2
+ import fs from 'fs';
3
+ import {Stats} from 'fs'
4
+ import path from 'path'
5
+
6
+ // Callback definition for recurseDirectory
7
+ export interface RecurseCB {
8
+ (filepath: string, stats: Stats): boolean|void;
9
+ }
10
+
11
+ // Recurse a directory, calling back to a 'for-each' callback for all files in tree
12
+ export function recurseDirectory(dirpath:string, callback:RecurseCB) {
13
+ fs.readdirSync(dirpath).forEach((file:string) => {
14
+ const fpath = path.join(dirpath, file)
15
+ const stat = fs.lstatSync(fpath)
16
+ if(callback && !callback(fpath, stat)) {
17
+ if (stat.isDirectory()) {
18
+ recurseDirectory(fpath, callback)
19
+ }
20
+ }
21
+ })
22
+ }
23
+
24
+ // find the latest mod time for matching files in this folder tree
25
+ export function latestModification(dirpath:string, match:string) {
26
+ let latestTime = new Date(0);
27
+ if(fs.existsSync(dirpath)) {
28
+ recurseDirectory(dirpath, (filepath: string, stats: Stats) => {
29
+ let basefile = filepath.substring(filepath.lastIndexOf('/') + 1)
30
+ if (basefile.match(match)) {
31
+ if (stats.mtime > latestTime) latestTime = stats.mtime
32
+ }
33
+ })
34
+ }
35
+ return latestTime
36
+ }
@@ -0,0 +1,83 @@
1
+ // configuration for setting cloud provider credentials
2
+ // may expand later for other MistLift settings/preferences
3
+
4
+ import path from "path";
5
+ import fs from "fs";
6
+ import AWS, {fromIni} from '@aws-sdk/credential-providers';
7
+ import {NodeJsRuntimeStreamingBlobTypes} from "@smithy/types";
8
+
9
+ let s_liftConfigLoaded:LiftConfig|null = null;
10
+
11
+ // Defines the structure of the .mistlft json file
12
+ // All relevant configuration for the cloud host goes here
13
+ export class LiftConfig {
14
+ public cloudHost: string = "AWS" // all we support now
15
+ public awsIniProfile?: string
16
+ public awsPreferredRegion?:string
17
+ public awsNodeRuntime?: RuntimeType
18
+ public awsServiceRoleARN?:string
19
+ }
20
+ export type RuntimeType = "nodejs" | "nodejs4.3" | "nodejs6.10" | "nodejs8.10" | "nodejs10.x" | "nodejs12.x" | "nodejs14.x" | "nodejs16.x" | "java8" | "java8.al2" | "java11" | "python2.7" | "python3.6" | "python3.7" | "python3.8" | "python3.9" | "dotnetcore1.0" | "dotnetcore2.0" | "dotnetcore2.1" | "dotnetcore3.1" | "dotnet6" | "dotnet8" | "nodejs4.3-edge" | "go1.x" | "ruby2.5" | "ruby2.7" | "provided" | "provided.al2" | "nodejs18.x" | "python3.10" | "java17" | "ruby3.2" | "ruby3.3" | "python3.11" | "nodejs20.x" | "provided.al2023" | "python3.12" | "java21";
21
+
22
+ // Available for general use because, why not?
23
+ export function getUserHome() {
24
+ return process.env.HOME ?? process.env.HOMEPATH ?? process.env.USERPROFILE ?? "~";
25
+ }
26
+
27
+ export function LoadLiftConfig() : LiftConfig|null
28
+ {
29
+ if(!s_liftConfigLoaded) {
30
+
31
+ const mistlift = path.join(getUserHome(), ".mistlift");
32
+ let configJson = "{}";
33
+ if (fs.existsSync(mistlift)) {
34
+ try {
35
+ configJson = fs.readFileSync(mistlift).toString();
36
+ s_liftConfigLoaded = JSON.parse(configJson);
37
+ } catch (e: any) {
38
+ //
39
+ }
40
+ }
41
+ }
42
+ return s_liftConfigLoaded
43
+ }
44
+ export function resetLiftConfig()
45
+ {
46
+ s_liftConfigLoaded = null;
47
+ }
48
+
49
+ export function areSettingsAvailable():boolean {
50
+ const mistlift = path.join(getUserHome(), ".mistlift");
51
+ return fs.existsSync(mistlift);
52
+ }
53
+
54
+ export function getSettings() : LiftConfig {
55
+ if (s_liftConfigLoaded == null) LoadLiftConfig();
56
+ return s_liftConfigLoaded as LiftConfig
57
+ }
58
+
59
+ /**
60
+ * Return the credentials required for AWS based upon our config options.
61
+ *
62
+ * We will use the standard .aws config ini file, looked up by profile or default
63
+ * Prior attempts to have it do more than this failed, and resulted in a default profile choice anyway.
64
+ */
65
+ export function getAWSCredentials() : any
66
+ {
67
+ let config = LoadLiftConfig()
68
+ if(!config) {
69
+ // console.error("No .mistlift configuration found - using AWS default profile");
70
+ config = { cloudHost: "AWS", awsIniProfile: "default" }
71
+ }
72
+ if (config.cloudHost?.toUpperCase() !== "AWS")
73
+ {
74
+ throw Error("HostType Not Supported");
75
+ }
76
+ let credentials = {};
77
+ const profile = config.awsIniProfile ?? "default"
78
+ if(profile) {
79
+ credentials = fromIni({profile})
80
+ }
81
+
82
+ return credentials;
83
+ }
@@ -0,0 +1,95 @@
1
+
2
+ import * as path from 'path'
3
+ import * as fs from 'fs'
4
+ import {resolvePaths} from "./pathResolve";
5
+
6
+ export class VersionInfo
7
+ {
8
+ public major:number = 0
9
+ public minor:number = 0
10
+ public revision:number = 0
11
+ public suffix:string = "";
12
+
13
+ constructor(vstr:string) {
14
+ this.parse(vstr);
15
+ }
16
+
17
+ public toString = () => {
18
+ return `${this.major}.${this.minor}.${this.revision}`+(this.suffix? "-"+this.suffix : "")
19
+ }
20
+ public parse = (vstr:string) => {
21
+ let sn = vstr.indexOf('-');
22
+ let suffix = ""
23
+ if(sn !== -1) {
24
+ suffix = vstr.substring(sn+1);
25
+ vstr = vstr.substring(0,sn);
26
+ }
27
+ let parts = vstr.split('.');
28
+ while(parts.length < 3) parts.push("0");
29
+ this.major = parseInt(parts[0])
30
+ this.minor = parseInt(parts[1]);
31
+ this.revision = parseInt(parts[2]);
32
+ if(!isFinite(this.major)) this.major = 0;
33
+ if(!isFinite(this.minor)) this.minor = 0;
34
+ if(!isFinite(this.revision)) this.revision = 0;
35
+ this.suffix = suffix;
36
+ }
37
+ public equals(other: VersionInfo | string): boolean
38
+ {
39
+ // Note: not really a valid SemVer comparison
40
+ if(typeof other === 'string') other = new VersionInfo(other);
41
+ return this.major === other.major
42
+ && this.minor === other.minor
43
+ && this.revision === other.revision
44
+ }
45
+ public isGreaterThan(other:VersionInfo | string): boolean
46
+ {
47
+ // Note: not really a valid SemVer comparison
48
+ if(typeof other === 'string') other = new VersionInfo(other);
49
+ if(this.major > other.major) return true;
50
+ if(this.major < other.major) return false;
51
+ if(this.minor > other.minor) return true;
52
+ if(this.minor < other.minor) return false;
53
+ return (this.revision > other.revision);
54
+ }
55
+ }
56
+
57
+ // read version from Lift's package.json
58
+ export function getLiftVersion()
59
+ {
60
+ let pkg = path.join(__dirname, '..', '..', 'package.json');
61
+ return readPackageVersion(pkg);
62
+
63
+ }
64
+ // read version from project's package.json
65
+ export function getProjectVersion()
66
+ {
67
+ const projectPaths = resolvePaths();
68
+ return readPackageVersion(projectPaths.packagePath)
69
+
70
+ }
71
+
72
+ export function getProjectName()
73
+ {
74
+ const projectPaths = resolvePaths();
75
+ let pkgJson:{name?:string} = {}
76
+ try {
77
+ pkgJson = JSON.parse(fs.readFileSync(projectPaths.packagePath).toString())
78
+ }
79
+ catch(e) {}
80
+ return pkgJson.name;
81
+ }
82
+
83
+ function readPackageVersion(pkgPath:string):VersionInfo
84
+ {
85
+ let pkgJson:{version?:string} = {}
86
+ try {
87
+ pkgJson = JSON.parse(fs.readFileSync(pkgPath).toString())
88
+ }
89
+ catch(e) {}
90
+
91
+ return new VersionInfo(pkgJson.version ?? "")
92
+
93
+ }
94
+
95
+
File without changes
File without changes
File without changes
@@ -0,0 +1,34 @@
1
+
2
+ import Tap from "tap"
3
+
4
+ import * as path from 'path'
5
+ import {isNewer, isNewerFile} from "../fileCompare";
6
+
7
+ function test(t:any) {
8
+
9
+ // ./dir1/file.1 551 < ./dir2/file.1 552
10
+ // ./dir1/file.2 553 > ./dir2/file.1 552
11
+ // ./dir1 553 > ./dir2 552
12
+ // .dir1 (.1) 552 < ./dir2 552
13
+
14
+ const dir1 = path.join(__dirname, "dir1");
15
+ const dir2 = path.join(__dirname, "dir2");
16
+ const dir1File1 = path.join(dir1, "file.1");
17
+ const dir1File2 = path.join(dir1, "file.2");
18
+ const dir2File1 = path.join(dir2, "file.1");
19
+
20
+ let newer = isNewerFile(dir1File1, dir2File1)
21
+ t.equal(newer, false, "./dir1/file.1 < ./dir2/file.1")
22
+ newer = isNewerFile(dir1File2, dir2File1)
23
+ t.equal(newer, true, "./dir1/file.2 > ./dir2/file.1")
24
+ newer = isNewerFile(dir1, dir2);
25
+ t.ok(newer, "dir1 > dir2");
26
+ newer = isNewerFile(dir1, dir2, ".1");
27
+ t.ok(!newer, "./dir1 (.1) < ./dir2")
28
+
29
+
30
+ t.end()
31
+ }
32
+ Tap.test('IsNewer', t => {
33
+ test(t)
34
+ })
@@ -0,0 +1,18 @@
1
+ /** handles interaction for asking a question */
2
+
3
+ import * as ac from "ansi-colors"
4
+ const readlineSync = require("readline-sync")
5
+
6
+ export function ask(
7
+ desc:string, // describes the context
8
+ query:string, // asks the actual question
9
+ def:string // default answer
10
+
11
+ ) :string // answer provided
12
+ {
13
+ console.log(ac.dim.blue.italic(desc))
14
+ let answer = readlineSync.question(ac.bold.green(query) + ac.dim.grey(` [${def}] `)+ '? ')
15
+ if(!answer) answer = def
16
+ return answer
17
+ }
18
+
@@ -0,0 +1,38 @@
1
+
2
+ import {exec} from 'child_process'
3
+ import * as path from 'path'
4
+
5
+ export function executeCommand(cmd:string, args:any[], cwd = '', consolePass = false, env:any = {}):Promise<any> {
6
+ const out = {
7
+ stdStr: '',
8
+ errStr: '',
9
+ retcode: 0
10
+ }
11
+ return new Promise(resolve => {
12
+ let cmdstr = cmd + ' ' + args.join(' ')
13
+ // console.log('executing ', cmdstr, 'at', cwd)
14
+ const opts = {
15
+ cwd:cwd,
16
+ env: Object.assign(env, process.env)
17
+ }
18
+ const proc = exec(cmdstr, opts)
19
+ if(proc.stdout) proc.stdout.on('data', data => {
20
+ out.stdStr += data.toString()
21
+ if(consolePass) process.stdout.write(data.toString())
22
+ })
23
+ if(proc.stderr) proc.stderr.on('data', data => {
24
+ out.errStr += data.toString()
25
+ if(consolePass) process.stdout.write(data.toString())
26
+ })
27
+ proc.on('error', error => {
28
+ console.error(error)
29
+ if(!out.errStr) out.errStr = error.message
30
+ out.retcode = -1
31
+ resolve(out)
32
+ })
33
+ proc.on('close', code => {
34
+ out.retcode = code === null ? -1 : code
35
+ resolve(out)
36
+ })
37
+ })
38
+ }
@@ -0,0 +1,46 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import {recurseDirectory} from "./DirectoryUtils";
4
+
5
+ // true if filepath > mapped file in outDir (i.e. .ts > .js)
6
+ export function isNewer(filepath:string, outDir:string, mappingObj:any = {".ts": ".js"})
7
+ {
8
+ let basename = path.basename(filepath);
9
+ let ext = path.extname(filepath)
10
+ basename = basename.substring(0, basename.lastIndexOf(ext));
11
+ let newExt = "";
12
+
13
+ if(mappingObj[ext]) newExt = mappingObj[ext];
14
+
15
+ let outfile = path.join(outDir, basename+newExt);
16
+ return isNewerFile(filepath, outfile);
17
+ }
18
+
19
+ // true if filepath is newer than destpath
20
+ export function isNewerFile(filepath:string, destpath:string, srcFilter:string = "", dstFilter:string = "")
21
+ {
22
+ const sstat = fs.statSync(filepath);
23
+ const srcTime = sstat.isDirectory() ? latestInDirectory(filepath, srcFilter) : fs.statSync(filepath).mtime;
24
+ let destTime = new Date(0);
25
+ if(fs.existsSync((destpath))) {
26
+ const dstat = fs.statSync(destpath);
27
+ destTime = dstat.isDirectory() ? latestInDirectory(destpath, dstFilter) : fs.statSync(destpath).mtime;
28
+ }
29
+ return (srcTime >= destTime)
30
+ }
31
+ function latestInDirectory(dirPath:string, extFilter:string = "")
32
+ {
33
+ let newestTime = new Date(0);
34
+ recurseDirectory(dirPath, (filepath, stats) => {
35
+ if(stats.isFile()) {
36
+ const ext = path.extname(filepath);
37
+ if(!extFilter || ext === extFilter) {
38
+ if(stats.mtime > newestTime) {
39
+ newestTime = stats.mtime
40
+ }
41
+ }
42
+ }
43
+ })
44
+ return newestTime;
45
+
46
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Part 1 of two steps to build openApi docs.
3
+ * This reads values from our "definitions.json" format and creates a single array that step 2 will turn into
4
+ * openApi specifications.
5
+ */
6
+ import fs from 'fs'
7
+ import path from 'path'
8
+ import {recurseDirectory} from "../DirectoryUtils";
9
+ import {resolvePaths} from "../pathResolve";
10
+
11
+
12
+ export function gatherFunctionDefinitions():any[] {
13
+ const defs:any = []
14
+ try {
15
+ const projectPaths = resolvePaths();
16
+ if(!projectPaths.verified) return [];
17
+ const funcNames: string[] = [];
18
+ if(!fs.existsSync(projectPaths.functionPath)) return [];
19
+ let firstDepth = 0;
20
+ recurseDirectory(projectPaths.functionPath, (filepath, stats) => {
21
+ if (stats.isDirectory()) {
22
+ let depth = filepath.split(path.sep).length
23
+ if(!firstDepth) firstDepth = depth
24
+ if(firstDepth == depth) {
25
+ funcNames.push(path.basename(filepath));
26
+ }
27
+ }
28
+ })
29
+ if(projectPaths.functionPath.indexOf('functions') !== -1 ) {
30
+ for (let name of funcNames) {
31
+ let defPath = path.join(projectPaths.functionPath, name, 'src', 'definition.json')
32
+ if (fs.existsSync(defPath)) {
33
+ const content = fs.readFileSync(defPath).toString()
34
+ let buildPath = path.join(projectPaths.buildPath, 'functions', name, 'src', 'definition.json')
35
+ // fs.writeFileSync(buildPath, content); // use this opportunity to copy to build folder before we use it.
36
+ defs.push(JSON.parse(content))
37
+ } else {
38
+ console.error(`Definition file not found at ${defPath}`);
39
+ return [];
40
+ }
41
+ }
42
+ }
43
+ } catch(e: any) {
44
+ console.error("Exception in ApiBuildCollector", e)
45
+ }
46
+ return defs;
47
+ }
@@ -0,0 +1,21 @@
1
+
2
+ import * as path from 'path'
3
+ import {resolvePaths} from "../pathResolve";
4
+ import {recurseDirectory} from "../DirectoryUtils";
5
+
6
+
7
+ export function GetWebrootServePaths(): string[]
8
+ {
9
+ const projectPaths = resolvePaths();
10
+ const webroot = path.join(projectPaths.basePath, "webroot")
11
+
12
+ const list:string[] = [''];
13
+ recurseDirectory(webroot, (filepath, stats) => {
14
+ if(stats.isDirectory())
15
+ {
16
+ let relpath = filepath.substring(webroot.length);
17
+ if(list.indexOf(relpath) === -1) list.push(relpath);
18
+ }
19
+ })
20
+ return list;
21
+ }