@tremho/mist-lift 1.1.1 → 1.1.3-pre-release.1

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 (75) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +65 -65
  3. package/build/commands/actions/initQuestions.js +35 -33
  4. package/build/commands/actions/initQuestions.js.map +1 -1
  5. package/build/commands/build.js +1 -1
  6. package/build/commands/build.js.map +1 -1
  7. package/build/commands/create.js +1 -1
  8. package/build/commands/create.js.map +1 -1
  9. package/build/commands/deploy.js +1 -1
  10. package/build/commands/deploy.js.map +1 -1
  11. package/build/commands/info.js +1 -1
  12. package/build/commands/info.js.map +1 -1
  13. package/build/commands/init.js +2 -2
  14. package/build/commands/init.js.map +1 -1
  15. package/build/commands/package.js +2 -2
  16. package/build/commands/package.js.map +1 -1
  17. package/build/commands/publish.js +2 -2
  18. package/build/commands/publish.js.map +1 -1
  19. package/build/expressRoutes/functionBinder.js.map +1 -1
  20. package/build/integration-tests/quickstart-scenario.test.js +76 -0
  21. package/build/integration-tests/quickstart-scenario.test.js.map +1 -0
  22. package/build/lib/utils.js +1 -1
  23. package/build/lib/utils.js.map +1 -1
  24. package/build/lift.js +1 -1
  25. package/build/lift.js.map +1 -1
  26. package/package.json +79 -72
  27. package/src/commands/actions/initQuestions.ts +133 -131
  28. package/src/commands/actions/setupPackageJson.ts +32 -32
  29. package/src/commands/build.ts +170 -170
  30. package/src/commands/builtin/ApiDocMaker.ts +102 -102
  31. package/src/commands/builtin/BuiltInHandler.ts +47 -47
  32. package/src/commands/builtin/DeployBuiltInZip.ts +25 -25
  33. package/src/commands/builtin/StageWebrootZip.ts +36 -36
  34. package/src/commands/create.ts +52 -52
  35. package/src/commands/deploy.ts +161 -161
  36. package/src/commands/doctor.ts +106 -106
  37. package/src/commands/help.ts +178 -178
  38. package/src/commands/info.ts +42 -43
  39. package/src/commands/init.ts +61 -60
  40. package/src/commands/package.ts +234 -234
  41. package/src/commands/publish.ts +330 -330
  42. package/src/commands/settings.ts +73 -73
  43. package/src/commands/start.ts +43 -43
  44. package/src/commands/test.ts +37 -37
  45. package/src/commands/user.ts +20 -20
  46. package/src/expressRoutes/all.ts +99 -99
  47. package/src/expressRoutes/api.ts +22 -22
  48. package/src/expressRoutes/functionBinder.ts +155 -156
  49. package/src/integration-tests/quickstart-scenario.test.ts +74 -0
  50. package/src/lib/CaseUtils.ts +63 -63
  51. package/src/lib/DirectoryUtils.ts +34 -34
  52. package/src/lib/LiftConfig.ts +74 -74
  53. package/src/lib/LiftVersion.ts +87 -87
  54. package/src/lib/Tests/fileCompare.test.ts +35 -35
  55. package/src/lib/askQuestion.ts +17 -17
  56. package/src/lib/executeCommand.ts +45 -45
  57. package/src/lib/fileCompare.ts +55 -55
  58. package/src/lib/openAPI/ApiBuildCollector.ts +47 -47
  59. package/src/lib/openAPI/WebrootFileSupport.ts +19 -19
  60. package/src/lib/openAPI/openApiConstruction.ts +196 -196
  61. package/src/lib/pathResolve.ts +26 -26
  62. package/src/lib/utils.ts +43 -43
  63. package/src/lift.ts +87 -87
  64. package/templateData/function-definition-template +20 -20
  65. package/templateData/function-local-ts +16 -16
  66. package/templateData/function-main-ts +16 -16
  67. package/templateData/function-runmain-mjs +6 -6
  68. package/templateData/function-test-template +11 -11
  69. package/templateData/swagger-ui-bundle.js +2 -2
  70. package/templateData/swagger-ui-standalone-preset.js +2 -2
  71. package/templateData/swagger-ui.css +2 -2
  72. package/tsconfig.json +28 -28
  73. /package/build/commands/builtin/{prebuilt-zips → prebult-zips}/API.zip +0 -0
  74. /package/build/commands/builtin/{prebuilt-zips → prebult-zips}/FileServe.zip +0 -0
  75. /package/build/commands/builtin/{prebuilt-zips → prebult-zips}/Webroot.zip +0 -0
@@ -1,156 +1,155 @@
1
- /* eslint @typescript-eslint/no-var-requires: "off" */
2
- import path from 'path'
3
-
4
- import * as ac from 'ansi-colors'
5
- import { resolvePaths } from '../lib/pathResolve'
6
-
7
- import express from 'express'
8
- import { gatherFunctionDefinitions } from '../lib/openAPI/ApiBuildCollector'
9
- import { buildOpenApi } from '../lib/openAPI/openApiConstruction'
10
- import {Log} from '@tremho/inverse-y'
11
-
12
- const clearModule = require('clear-module')
13
- const router = express.Router()
14
-
15
- export function functionBinder (): void {
16
- const defs = gatherFunctionDefinitions()
17
- buildOpenApi(defs).then(() => { // creates apidoc.yaml for when /api is run
18
- const projectPaths = resolvePaths()
19
-
20
- for (const def of defs) {
21
- const { name, pathMap, allowedMethods } = def
22
- const methods = allowedMethods.split(',')
23
- for (let method of methods) {
24
- try {
25
- method = method.trim().toLowerCase()
26
- const rpath = path.join(projectPaths.buildPath, 'functions', name, 'src', 'main.js')
27
- clearModule(rpath)
28
- const { start } = require(rpath)
29
-
30
- let entryRoot: string = pathMap
31
- const n = entryRoot.indexOf('/{')
32
- if (n !== -1) entryRoot = entryRoot.substring(0, n) + '/*'
33
-
34
- const callHandler = (pathMap: string, req: any, res: any): void => {
35
- const event = requestToEvent(pathMap, req)
36
- Promise.resolve(start(event, null, null)).then(respOut => {
37
- handleResponse(res, respOut)
38
- }).catch<any>((reason: any) => undefined)
39
- }
40
-
41
- if (method === 'get') {
42
- router.get(entryRoot, (req, res) => callHandler(pathMap, req, res))
43
- } else if (method === 'post') {
44
- router.post(entryRoot, (req, res) => callHandler(pathMap, req, res))
45
- } else if (method === 'put') {
46
- router.put(entryRoot, (req, res) => callHandler(pathMap, req, res))
47
- } else if (method === 'patch') {
48
- router.patch(entryRoot, (req, res) => callHandler(pathMap, req, res))
49
- } else if (method === 'delete') {
50
- router.delete(entryRoot, (req, res) => callHandler(pathMap, req, res))
51
- } else {
52
- console.log(ac.red.bold('Cannot map method ') + ac.blue.bold(method))
53
- }
54
- }
55
- catch(e:any) {
56
- Log.Error(ac.bold.red(e.message.split('\n')[0]));
57
- }
58
- }
59
- }
60
- }).catch<any>((reason: any) => undefined)
61
- }
62
-
63
- function requestToEvent (template: string, req: any): any {
64
- let path: string = req.originalUrl ?? ''
65
- const qi = path.indexOf('?')
66
- if (qi !== -1) path = path.substring(0, qi)
67
- const ptci: number = path.indexOf('://') + 3
68
- const ei: number = path.indexOf('/', ptci)
69
- let host: string = path.substring(0, ei)
70
- if (ptci < 3) {
71
- host = req.headers?.host ?? req.headers?.origin ?? req.headers?.referer ?? ''
72
- if(!host.startsWith('http')) {
73
- if(!host.startsWith('https')) {
74
- host = 'http://'+host;
75
- }
76
- }
77
- }
78
- const cookies: any = {}
79
- const cookieString = req.headers?.cookie ?? ''
80
- const crumbs = cookieString.split(';')
81
- for (const c of crumbs) {
82
- const pair: string[] = c.split('=')
83
- if (pair.length === 2) cookies[pair[0]] = pair[1]
84
- }
85
- const parameters: any = {}
86
- const tslots = template.split('/')
87
- const pslots = path.split('/')
88
- for (let i = 0; i < tslots.length; i++) {
89
- const brknm = (tslots[i] ?? '').trim()
90
- if (brknm.charAt(0) === '{') {
91
- const pn = brknm.substring(1, brknm.length - 1)
92
- parameters[pn] = (pslots[i] ?? '').trim()
93
- }
94
- }
95
- for (const p of Object.getOwnPropertyNames(req.query)) {
96
- parameters[p] = req.query[p]
97
- }
98
- const eventOut: any = {
99
- requestContext: req,
100
- request: {
101
- originalUrl: host + (req.originalUrl as string),
102
- headers: req.headers
103
- },
104
- body: req.body,
105
- cookies,
106
- parameters
107
- }
108
- return eventOut
109
- }
110
- function handleResponse (res: any, resp: any): void {
111
- // console.log(">>>>>>>> Handling response >>>>>>>>>")
112
-
113
- if (resp !== undefined) {
114
- if (resp.cookies !== undefined) {
115
- // console.log("--- see cookies", resp.cookies)
116
- let cookies: any = []
117
- if (Array.isArray(resp.cookies)) {
118
- cookies = resp.cookies
119
- } else {
120
- const age: number = resp.cookies.expireSeconds ?? 60 // 1 minute
121
- delete resp.expireSeconds
122
- Object.getOwnPropertyNames(resp.cookies).forEach(name => {
123
- const value: string = resp.cookies[name]
124
- cookies.push(`${name}=${value}; Max-Age=${age}; `)
125
- })
126
- }
127
- // console.log("cookies being set", cookies)
128
- res.setHeader('set-cookie', cookies)
129
- delete resp.cookies
130
- }
131
- if (resp.headers !== undefined) {
132
- // if (resp.statusCode === 301) ClogTrace("Redirecting...");
133
- for (const hdr of Object.getOwnPropertyNames(resp.headers)) {
134
- // ClogTrace("Setting header ", hdr, resp.headers[hdr]);
135
- res.setHeader(hdr, resp.headers[hdr])
136
- }
137
- delete resp.headers
138
- // console.log("past setting headers");
139
- }
140
- if (resp.statusCode !== undefined) {
141
- res.statusCode = resp.statusCode
142
- delete resp.statusCode
143
- }
144
-
145
- if (resp.contentType !== undefined) {
146
- res.setHeader('Content-Type', resp.contentType)
147
- delete resp.contentType
148
- }
149
- if (resp.body !== undefined) resp = resp.body
150
- }
151
- // console.log("headers to be sent", res.getHeaders())
152
- // console.log("-- Sending response", resp)
153
- res.send(resp)
154
- }
155
-
156
- export default router
1
+ /* eslint @typescript-eslint/no-var-requires: "off" */
2
+ import path from 'path'
3
+
4
+ import * as ac from 'ansi-colors'
5
+ import { resolvePaths } from '../lib/pathResolve'
6
+
7
+ import express from 'express'
8
+ import { gatherFunctionDefinitions } from '../lib/openAPI/ApiBuildCollector'
9
+ import { buildOpenApi } from '../lib/openAPI/openApiConstruction'
10
+ import { Log } from '@tremho/inverse-y'
11
+
12
+ const clearModule = require('clear-module')
13
+ const router = express.Router()
14
+
15
+ export function functionBinder (): void {
16
+ const defs = gatherFunctionDefinitions()
17
+ buildOpenApi(defs).then(() => { // creates apidoc.yaml for when /api is run
18
+ const projectPaths = resolvePaths()
19
+
20
+ for (const def of defs) {
21
+ const { name, pathMap, allowedMethods } = def
22
+ const methods = allowedMethods.split(',')
23
+ for (let method of methods) {
24
+ try {
25
+ method = method.trim().toLowerCase()
26
+ const rpath = path.join(projectPaths.buildPath, 'functions', name, 'src', 'main.js')
27
+ clearModule(rpath)
28
+ const { start } = require(rpath)
29
+
30
+ let entryRoot: string = pathMap
31
+ const n = entryRoot.indexOf('/{')
32
+ if (n !== -1) entryRoot = entryRoot.substring(0, n) + '/*'
33
+
34
+ const callHandler = (pathMap: string, req: any, res: any): void => {
35
+ const event = requestToEvent(pathMap, req)
36
+ Promise.resolve(start(event, null, null)).then(respOut => {
37
+ handleResponse(res, respOut)
38
+ }).catch<any>((reason: any) => undefined)
39
+ }
40
+
41
+ if (method === 'get') {
42
+ router.get(entryRoot, (req, res) => callHandler(pathMap, req, res))
43
+ } else if (method === 'post') {
44
+ router.post(entryRoot, (req, res) => callHandler(pathMap, req, res))
45
+ } else if (method === 'put') {
46
+ router.put(entryRoot, (req, res) => callHandler(pathMap, req, res))
47
+ } else if (method === 'patch') {
48
+ router.patch(entryRoot, (req, res) => callHandler(pathMap, req, res))
49
+ } else if (method === 'delete') {
50
+ router.delete(entryRoot, (req, res) => callHandler(pathMap, req, res))
51
+ } else {
52
+ console.log(ac.red.bold('Cannot map method ') + ac.blue.bold(method))
53
+ }
54
+ } catch (e: any) {
55
+ Log.Error(ac.bold.red(e.message.split('\n')[0]))
56
+ }
57
+ }
58
+ }
59
+ }).catch<any>((reason: any) => undefined)
60
+ }
61
+
62
+ function requestToEvent (template: string, req: any): any {
63
+ let path: string = req.originalUrl ?? ''
64
+ const qi = path.indexOf('?')
65
+ if (qi !== -1) path = path.substring(0, qi)
66
+ const ptci: number = path.indexOf('://') + 3
67
+ const ei: number = path.indexOf('/', ptci)
68
+ let host: string = path.substring(0, ei)
69
+ if (ptci < 3) {
70
+ host = req.headers?.host ?? req.headers?.origin ?? req.headers?.referer ?? ''
71
+ if (!host.startsWith('http')) {
72
+ if (!host.startsWith('https')) {
73
+ host = 'http://' + host
74
+ }
75
+ }
76
+ }
77
+ const cookies: any = {}
78
+ const cookieString = req.headers?.cookie ?? ''
79
+ const crumbs = cookieString.split(';')
80
+ for (const c of crumbs) {
81
+ const pair: string[] = c.split('=')
82
+ if (pair.length === 2) cookies[pair[0]] = pair[1]
83
+ }
84
+ const parameters: any = {}
85
+ const tslots = template.split('/')
86
+ const pslots = path.split('/')
87
+ for (let i = 0; i < tslots.length; i++) {
88
+ const brknm = (tslots[i] ?? '').trim()
89
+ if (brknm.charAt(0) === '{') {
90
+ const pn = brknm.substring(1, brknm.length - 1)
91
+ parameters[pn] = (pslots[i] ?? '').trim()
92
+ }
93
+ }
94
+ for (const p of Object.getOwnPropertyNames(req.query)) {
95
+ parameters[p] = req.query[p]
96
+ }
97
+ const eventOut: any = {
98
+ requestContext: req,
99
+ request: {
100
+ originalUrl: host + (req.originalUrl as string),
101
+ headers: req.headers
102
+ },
103
+ body: req.body,
104
+ cookies,
105
+ parameters
106
+ }
107
+ return eventOut
108
+ }
109
+ function handleResponse (res: any, resp: any): void {
110
+ // console.log(">>>>>>>> Handling response >>>>>>>>>")
111
+
112
+ if (resp !== undefined) {
113
+ if (resp.cookies !== undefined) {
114
+ // console.log("--- see cookies", resp.cookies)
115
+ let cookies: any = []
116
+ if (Array.isArray(resp.cookies)) {
117
+ cookies = resp.cookies
118
+ } else {
119
+ const age: number = resp.cookies.expireSeconds ?? 60 // 1 minute
120
+ delete resp.expireSeconds
121
+ Object.getOwnPropertyNames(resp.cookies).forEach(name => {
122
+ const value: string = resp.cookies[name]
123
+ cookies.push(`${name}=${value}; Max-Age=${age}; `)
124
+ })
125
+ }
126
+ // console.log("cookies being set", cookies)
127
+ res.setHeader('set-cookie', cookies)
128
+ delete resp.cookies
129
+ }
130
+ if (resp.headers !== undefined) {
131
+ // if (resp.statusCode === 301) ClogTrace("Redirecting...");
132
+ for (const hdr of Object.getOwnPropertyNames(resp.headers)) {
133
+ // ClogTrace("Setting header ", hdr, resp.headers[hdr]);
134
+ res.setHeader(hdr, resp.headers[hdr])
135
+ }
136
+ delete resp.headers
137
+ // console.log("past setting headers");
138
+ }
139
+ if (resp.statusCode !== undefined) {
140
+ res.statusCode = resp.statusCode
141
+ delete resp.statusCode
142
+ }
143
+
144
+ if (resp.contentType !== undefined) {
145
+ res.setHeader('Content-Type', resp.contentType)
146
+ delete resp.contentType
147
+ }
148
+ if (resp.body !== undefined) resp = resp.body
149
+ }
150
+ // console.log("headers to be sent", res.getHeaders())
151
+ // console.log("-- Sending response", resp)
152
+ res.send(resp)
153
+ }
154
+
155
+ export default router
@@ -0,0 +1,74 @@
1
+
2
+ /* eslint @typescript-eslint/no-floating-promises: "off" */
3
+
4
+ import Tap from 'tap'
5
+
6
+ import fs from 'fs'
7
+ import path from 'path'
8
+
9
+ import { doInit } from '../commands/init'
10
+ import { doCreate } from '../commands/create'
11
+ import { doBuildAsync } from '../commands/build'
12
+ import { doDeployAsync } from '../commands/deploy'
13
+ import { doPublishAsync } from '../commands/publish'
14
+
15
+ import axios from 'axios'
16
+
17
+ async function test (t: any): Promise<void> {
18
+ const testMessage = `test on ${Date.now()}`
19
+ // start clean
20
+ fs.rmSync('./QSTest', { recursive: true })
21
+ // init
22
+ await doInit('QSTest', true)
23
+
24
+ // verify project got made
25
+ t.ok(fs.existsSync('./QSTest'), 'created test project')
26
+ // verify we have a functions dir
27
+ t.ok(fs.existsSync(path.join('./QSTest', 'functions')), 'functions exists')
28
+ // verify we have a node_modules dir
29
+ t.ok(fs.existsSync(path.join('./QSTest', 'node_modules')), 'node_modules exists')
30
+ // verify we have a webroot dir
31
+ t.ok(fs.existsSync(path.join('./QSTest', 'webroot')), 'webroot exists')
32
+ // verify we have a package.json
33
+ t.ok(fs.existsSync(path.join('./QSTest', 'package.json')), 'package.json exists')
34
+ // verify we have webroot/docs
35
+ t.ok(fs.existsSync(path.join('./QSTest', 'webroot', 'docs', 'apidoc.yaml')), 'yaml exists')
36
+ t.ok(fs.existsSync(path.join('./QSTest', 'webroot', 'docs', 'swagger-ui-bundle.js')), 'js exists')
37
+ t.ok(fs.existsSync(path.join('./QSTest', 'webroot', 'docs', 'swagger-ui-standalone-preset.js')), 'js exists')
38
+ t.ok(fs.existsSync(path.join('./QSTest', 'webroot', 'docs', 'swagger-ui.css')), 'css exists')
39
+
40
+ // create
41
+ process.chdir('./QSTest')
42
+ await doCreate('IntegrationTest')
43
+ t.ok(fs.existsSync(path.join('functions', 'IntegrationTest')), 'function exists')
44
+ t.ok(fs.existsSync(path.join('functions', 'IntegrationTest', 'src', 'definition.json')), 'definition.json exists')
45
+ t.ok(fs.existsSync(path.join('functions', 'IntegrationTest', 'src', 'local.ts')), 'local.ts exists')
46
+ const maints = path.join('functions', 'IntegrationTest', 'src', 'main.ts')
47
+ t.ok(fs.existsSync(maints), 'main.ts exists')
48
+ const content = fs.readFileSync(maints).toString().replace('Hello, World!', testMessage)
49
+ fs.writeFileSync(maints, content)
50
+
51
+ // build
52
+ await doBuildAsync(['--clean'])
53
+
54
+ await doDeployAsync([])
55
+
56
+ await doPublishAsync()
57
+
58
+ const publishedJson = fs.readFileSync('.published').toString()
59
+ const publishedInfo = JSON.parse(publishedJson)
60
+ const apiUrl: string = publishedInfo.url
61
+ t.ok(apiUrl.length > 0, 'url exists')
62
+ const testUrl = apiUrl + '/integrationtest'
63
+
64
+ const resp: any = await axios.get(testUrl)
65
+ t.ok(resp.status === 200, 'status is 200')
66
+ const data: string = resp.data.toString()
67
+ t.ok(data === testMessage, 'saw expected data')
68
+
69
+ t.end()
70
+ }
71
+
72
+ Tap.test('Integration Quickstart', async t => {
73
+ await test(t)
74
+ })
@@ -1,63 +1,63 @@
1
-
2
- // separate a case-joined string into a string of space-separated words
3
- export function separated (
4
- instr: string
5
- ): string {
6
- let outstr = ''
7
- for (let i = 0; i < instr.length; i++) {
8
- const c = instr.charAt(i)
9
- if (!isAlphaNum(c) || isUpperCase(c)) {
10
- outstr += ' '
11
- }
12
- outstr += c.toLowerCase()
13
- }
14
-
15
- return outstr.trim()
16
- }
17
-
18
- function isUpperCase (instr: string): boolean {
19
- return instr?.toUpperCase() === instr
20
- }
21
- function isAlphaNum (char: string): boolean {
22
- let alnum = false
23
- const cc = char.toUpperCase().charCodeAt(0)
24
- if (cc >= 'A'.charCodeAt(0) && cc <= 'Z'.charCodeAt(0)) alnum = true
25
- if (cc >= '0'.charCodeAt(0) && cc <= '9'.charCodeAt(0)) alnum = true
26
- return alnum
27
- }
28
-
29
- export function camelCase (
30
- instr: string
31
- ): string {
32
- const seps = separated(instr).split(' ')
33
- for (let i = 1; i < seps.length; i++) {
34
- seps[i] = seps[i].charAt(0).toUpperCase() + seps[i].substring(1)
35
- }
36
- return seps.join('')
37
- }
38
-
39
- export function pascalCase (
40
- instr: string
41
- ): string {
42
- const seps = separated(instr).split(' ')
43
- for (let i = 0; i < seps.length; i++) {
44
- seps[i] = seps[i].charAt(0).toUpperCase() + seps[i].substring(1)
45
- }
46
- return seps.join('')
47
- }
48
-
49
- export function snakeCase (
50
- instr: string
51
- ): string {
52
- return dashCase(instr, '_')
53
- }
54
- export function dashCase (
55
- instr: string,
56
- dashChar: string = '-',
57
- allCaps: boolean = false
58
- ): string {
59
- const seps = separated(instr).split(' ')
60
- let out = seps.join(dashChar)
61
- if (allCaps) out = out.toUpperCase()
62
- return out
63
- }
1
+
2
+ // separate a case-joined string into a string of space-separated words
3
+ export function separated (
4
+ instr: string
5
+ ): string {
6
+ let outstr = ''
7
+ for (let i = 0; i < instr.length; i++) {
8
+ const c = instr.charAt(i)
9
+ if (!isAlphaNum(c) || isUpperCase(c)) {
10
+ outstr += ' '
11
+ }
12
+ outstr += c.toLowerCase()
13
+ }
14
+
15
+ return outstr.trim()
16
+ }
17
+
18
+ function isUpperCase (instr: string): boolean {
19
+ return instr?.toUpperCase() === instr
20
+ }
21
+ function isAlphaNum (char: string): boolean {
22
+ let alnum = false
23
+ const cc = char.toUpperCase().charCodeAt(0)
24
+ if (cc >= 'A'.charCodeAt(0) && cc <= 'Z'.charCodeAt(0)) alnum = true
25
+ if (cc >= '0'.charCodeAt(0) && cc <= '9'.charCodeAt(0)) alnum = true
26
+ return alnum
27
+ }
28
+
29
+ export function camelCase (
30
+ instr: string
31
+ ): string {
32
+ const seps = separated(instr).split(' ')
33
+ for (let i = 1; i < seps.length; i++) {
34
+ seps[i] = seps[i].charAt(0).toUpperCase() + seps[i].substring(1)
35
+ }
36
+ return seps.join('')
37
+ }
38
+
39
+ export function pascalCase (
40
+ instr: string
41
+ ): string {
42
+ const seps = separated(instr).split(' ')
43
+ for (let i = 0; i < seps.length; i++) {
44
+ seps[i] = seps[i].charAt(0).toUpperCase() + seps[i].substring(1)
45
+ }
46
+ return seps.join('')
47
+ }
48
+
49
+ export function snakeCase (
50
+ instr: string
51
+ ): string {
52
+ return dashCase(instr, '_')
53
+ }
54
+ export function dashCase (
55
+ instr: string,
56
+ dashChar: string = '-',
57
+ allCaps: boolean = false
58
+ ): string {
59
+ const seps = separated(instr).split(' ')
60
+ let out = seps.join(dashChar)
61
+ if (allCaps) out = out.toUpperCase()
62
+ return out
63
+ }
@@ -1,34 +1,34 @@
1
-
2
- import fs, { Stats } from 'fs'
3
- import path from 'path'
4
-
5
- // Callback definition for recurseDirectory
6
- export type RecurseCB = (filepath: string, stats: Stats) => boolean
7
-
8
- // Recurse a directory, calling back to a 'for-each' callback for all files in tree
9
- export function recurseDirectory (dirpath: string, callback?: RecurseCB): void {
10
- fs.readdirSync(dirpath).forEach((file: string) => {
11
- const fpath = path.join(dirpath, file)
12
- const stat = fs.lstatSync(fpath)
13
- if ((callback != null) && !callback(fpath, stat)) {
14
- if (stat.isDirectory()) {
15
- recurseDirectory(fpath, callback)
16
- }
17
- }
18
- })
19
- }
20
-
21
- // find the latest mod time for matching files in this folder tree
22
- export function latestModification (dirpath: string, match: string): Date {
23
- let latestTime = new Date(0)
24
- if (fs.existsSync(dirpath)) {
25
- recurseDirectory(dirpath, (filepath: string, stats: Stats) => {
26
- const basefile = filepath.substring(filepath.lastIndexOf('/') + 1)
27
- if (basefile.match(match) != null) {
28
- if (stats.mtime > latestTime) latestTime = stats.mtime
29
- }
30
- return false
31
- })
32
- }
33
- return latestTime
34
- }
1
+
2
+ import fs, { Stats } from 'fs'
3
+ import path from 'path'
4
+
5
+ // Callback definition for recurseDirectory
6
+ export type RecurseCB = (filepath: string, stats: Stats) => boolean
7
+
8
+ // Recurse a directory, calling back to a 'for-each' callback for all files in tree
9
+ export function recurseDirectory (dirpath: string, callback?: RecurseCB): void {
10
+ fs.readdirSync(dirpath).forEach((file: string) => {
11
+ const fpath = path.join(dirpath, file)
12
+ const stat = fs.lstatSync(fpath)
13
+ if ((callback != null) && !callback(fpath, stat)) {
14
+ if (stat.isDirectory()) {
15
+ recurseDirectory(fpath, callback)
16
+ }
17
+ }
18
+ })
19
+ }
20
+
21
+ // find the latest mod time for matching files in this folder tree
22
+ export function latestModification (dirpath: string, match: string): Date {
23
+ let latestTime = new Date(0)
24
+ if (fs.existsSync(dirpath)) {
25
+ recurseDirectory(dirpath, (filepath: string, stats: Stats) => {
26
+ const basefile = filepath.substring(filepath.lastIndexOf('/') + 1)
27
+ if (basefile.match(match) != null) {
28
+ if (stats.mtime > latestTime) latestTime = stats.mtime
29
+ }
30
+ return false
31
+ })
32
+ }
33
+ return latestTime
34
+ }