@architect/inventory 3.1.1 → 3.2.0-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.
package/changelog.md CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  ---
4
4
 
5
+ ## [3.2.0] 2022-07-24
6
+
7
+ ### Added
8
+
9
+ - Added support for new setter plugin APIs, specifically: `@proxy`, `@shared`, `@static`, `@tables`, `@tables-indexes`, `@views`
10
+
11
+
12
+ ### Changed
13
+
14
+ - `@tables` and `@tables-indexes` can now accept lower case key types (e.g. `*string` instead of `*String`)
15
+ - `@tables` and `@tables-indexes` can also accept `*` and `**` as a shortcut for string-type primary and sort keys
16
+
17
+
18
+ ### Fixed
19
+
20
+ - Fixed issue where Lambdas created by plugins that returned arrays did not have their `plugin` and `type` properties set
21
+ - Fixed issue where an absolute path in `@shared|views` `src` would incorrectly resolve
22
+ - Fixed issue where `@views` might incorrectly return a validation error when only HTTP setter plugins are used to define `@http` routes
23
+
24
+ ---
25
+
5
26
  ## [3.1.1] 2022-05-09
6
27
 
7
28
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@architect/inventory",
3
- "version": "3.1.1",
3
+ "version": "3.2.0-RC.0",
4
4
  "description": "Architect project resource enumeration utility",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -2,10 +2,10 @@ let { join } = require('path')
2
2
  let { existsSync } = require('fs')
3
3
  let { is, normalizeSrc, pragmas, tidyError, validationPatterns } = require('../../lib')
4
4
  let { lambdas } = pragmas
5
- let nonLambdaSetters = [ 'customLambdas', 'env', 'runtimes' ]
5
+ let nonLambdaSetters = [ 'customLambdas', 'env', 'proxy', 'runtimes', 'shared', 'static', 'views', 'tables', 'tables-indexes' ]
6
6
  let setters = [ ...lambdas, ...nonLambdaSetters ]
7
7
  let pluginMethods = [ 'deploy', 'sandbox' ] // TODO add more!
8
- let reservedNames = [ '_methods', 'events', 'queues', 'static', 'tables' ]
8
+ let reservedNames = [ '_methods' ]
9
9
 
10
10
  module.exports = function getPluginModules ({ arc, inventory, errors }) {
11
11
  if (!arc?.plugins?.length && !arc?.macros?.length) return null
@@ -107,7 +107,7 @@ function getPath (cwd, srcDir, name) {
107
107
  join(cwd, 'node_modules', name),
108
108
  join(cwd, 'node_modules', `@${name}`),
109
109
  ]
110
- if (existsSync(paths[0])) return paths[0]
110
+ /**/ if (existsSync(paths[0])) return paths[0]
111
111
  else if (existsSync(paths[1])) return paths[1]
112
112
  else if (existsSync(paths[2])) return paths[2]
113
113
  else if (existsSync(paths[3])) return paths[3]
@@ -36,9 +36,10 @@ function populateLambda (type, params) {
36
36
  return []
37
37
  }
38
38
  if (is.array(result)) {
39
- result.forEach(item => {
39
+ result.forEach((item, i) => {
40
40
  item.plugin = fn.plugin
41
41
  item.type = fn.type
42
+ result[i] = item
42
43
  })
43
44
  }
44
45
  else {
@@ -0,0 +1,117 @@
1
+ let { deepFrozenCopy } = require('@architect/utils')
2
+ let { is } = require('../../../lib')
3
+
4
+ /**
5
+ * Build out resource pragmas (e.g. `@tables`) via plugins
6
+ * Returns an array of resources
7
+ */
8
+ function resources (params) {
9
+ let { errors, template, plugins, inventory, type, valid } = params
10
+ if (plugins) {
11
+ let invCopy = deepFrozenCopy(inventory)
12
+ let pluginResults = plugins.flatMap(fn => {
13
+ try {
14
+ var result = fn({ arc: invCopy._project.arc, inventory: { inv: invCopy } })
15
+ }
16
+ catch (err) {
17
+ err.message = `Setter plugin exception: plugin: ${fn.plugin}, method: set.${type}`
18
+ + `\n` + err.message
19
+ throw err
20
+ }
21
+ if (!result ||
22
+ (!is.object(result) && !is.array(result)) ||
23
+ (is.array(result) && result.some(r => !is.object(r)))) {
24
+ errors.push(`Setter plugins must return a valid response: plugin: ${fn.plugin}, method: set.${type}`)
25
+ return []
26
+ }
27
+ if (is.array(result)) {
28
+ result.forEach((item, i) => {
29
+ item = populateTemplate(template, item)
30
+ item.plugin = fn.plugin
31
+ item.type = fn.type
32
+ result[i] = item
33
+ })
34
+ }
35
+ else {
36
+ result = populateTemplate(template, result)
37
+ result.plugin = fn.plugin
38
+ result.type = fn.type
39
+ }
40
+ return result
41
+ })
42
+ // Validation pass
43
+ let validationErrors = []
44
+ pluginResults.forEach(item => {
45
+ let errors = validate(item, valid, type)
46
+ validationErrors.push(...errors)
47
+ })
48
+ if (validationErrors.length) {
49
+ errors = errors.push(...validationErrors)
50
+ return []
51
+ }
52
+ return pluginResults
53
+ }
54
+ return
55
+ }
56
+
57
+ function populateTemplate (template, item) {
58
+ let newItem = JSON.parse(JSON.stringify(template))
59
+ Object.entries(item).forEach(([ setting, value ]) => {
60
+ if (is.defined(value)) newItem[setting] = value
61
+ })
62
+ return newItem
63
+ }
64
+
65
+ function validate (item, valid, type) {
66
+ if (!valid) return []
67
+ return Object.entries(valid).map(([ setting, value ]) => {
68
+ if (!is[value](item[setting])) return `Invalid plugin-generated @${type} resource: ${setting}: ${item[setting]}`
69
+ }).filter(Boolean)
70
+ }
71
+
72
+ /**
73
+ * Build out settings pragmas (e.g. `@static`) via plugins
74
+ * Returns an object of settings
75
+ */
76
+ function settings (params) {
77
+ let { errors, settings, plugins, inventory, type, valid } = params
78
+ if (plugins) {
79
+ let invCopy = deepFrozenCopy(inventory)
80
+ let pluginSettings = settings
81
+ let foundError = false
82
+ plugins.forEach(fn => {
83
+ try {
84
+ var result = fn({ arc: invCopy._project.arc, inventory: { inv: invCopy } })
85
+ }
86
+ catch (err) {
87
+ err.message = `Setter plugin exception: plugin: ${fn.plugin}, method: set.${type}`
88
+ + `\n` + err.message
89
+ throw err
90
+ }
91
+ if (!result || !is.object(result)) {
92
+ errors.push(`Setter plugins must return a valid response: plugin: ${fn.plugin}, method: set.${type}`)
93
+ foundError = true
94
+ }
95
+ else {
96
+ Object.entries(result).forEach(([ setting, value ]) => {
97
+ if (is.defined(settings[setting]) && is.defined(value)) {
98
+ pluginSettings[setting] = value
99
+ }
100
+ })
101
+ }
102
+ })
103
+ if (foundError) return null
104
+
105
+ // Validation pass
106
+ let validationErrors = validate(pluginSettings, valid, type)
107
+ if (validationErrors.length) {
108
+ errors = errors.push(...validationErrors)
109
+ return null
110
+ }
111
+
112
+ return pluginSettings
113
+ }
114
+ return settings
115
+ }
116
+
117
+ module.exports = { resources, settings }
@@ -1,19 +1,43 @@
1
+ let populate = require('./populate-other')
1
2
  let validate = require('./validate')
2
3
 
3
- module.exports = function configureProxy ({ arc, errors }) {
4
- if (arc.proxy && !arc.http) {
5
- errors.push('@proxy requires @http')
4
+ module.exports = function configureProxy ({ arc, inventory, errors }) {
5
+ let proxySetters = inventory.plugins?._methods?.set?.proxy
6
+ let httpSetters = inventory.plugins?._methods?.set?.http
7
+ if ((arc.proxy || proxySetters) &&
8
+ (!arc.http && !httpSetters)) {
9
+ errors.push('Specifying @proxy requires specifying @http')
6
10
  return null
7
11
  }
8
- if (!arc.proxy || !arc.http) return null
12
+ if (!arc.proxy && !proxySetters) return null
9
13
 
10
- let proxy = {}
11
- let envs = [ 'testing', 'staging', 'production' ]
12
- envs.forEach(env => {
13
- let setting = arc.proxy.find(s => (s[0] && s[0] === env) && s[1])
14
- if (!setting) errors.push(`@proxy ${env} environment not found or invalid`)
15
- else proxy[env] = setting[1]
14
+ let proxy = {
15
+ testing: null,
16
+ staging: null,
17
+ production: null,
18
+ }
19
+
20
+ proxy = populate.settings({
21
+ errors,
22
+ settings: proxy,
23
+ plugins: proxySetters,
24
+ inventory,
25
+ type: 'proxy',
26
+ valid: {
27
+ testing: 'string',
28
+ staging: 'string',
29
+ production: 'string',
30
+ },
16
31
  })
32
+ if (proxy === null) return null
33
+
34
+ if (arc?.proxy?.length) {
35
+ Object.keys(proxy).forEach(env => {
36
+ let setting = arc.proxy.find(s => (s[0] && s[0] === env) && s[1])
37
+ if (!setting) errors.push(`@proxy ${env} environment not found or invalid`)
38
+ else proxy[env] = setting[1]
39
+ })
40
+ }
17
41
 
18
42
  validate.proxy(proxy, errors)
19
43
 
@@ -1,4 +1,5 @@
1
1
  let { join } = require('path')
2
+ let populate = require('./populate-other')
2
3
  let validate = require('./validate')
3
4
  let { is, pragmas } = require('../../lib')
4
5
  let lambdas = pragmas.lambdas.concat('customLambdas')
@@ -9,20 +10,34 @@ module.exports = function configureShared ({ arc, pragmas, inventory, errors })
9
10
  let { cwd, src: projSrc } = inventory._project
10
11
  let src = join(projSrc, 'shared')
11
12
  let shared = {
12
- src,
13
- shared: [] // Revert to null later if none are defined
13
+ src: null,
14
+ shared: []
15
+ }
16
+
17
+ let foundSrcSetting = false
18
+ let pluginSrc = populate.settings({
19
+ errors,
20
+ settings: shared,
21
+ plugins: inventory.plugins?._methods?.set?.shared,
22
+ inventory,
23
+ type: 'shared',
24
+ valid: { src: 'string' },
25
+ })
26
+ // Shared setters only support src, and do not support specifying Lambdas
27
+ // Lambda paths have not yet been reified in Inventory
28
+ if (is.string(pluginSrc?.src)) {
29
+ shared.src = pluginSrc.src
30
+ foundSrcSetting = true
14
31
  }
15
32
 
16
33
  // First pass to get + check shared folder (if any)
17
- let foundSrc = false
18
34
  if (arc?.shared?.length) {
19
35
  for (let share of arc.shared) {
20
36
  if (is.array(share)) {
21
37
  let key = share[0]?.toLowerCase()
22
38
  if (key === 'src' && is.string(share[1])) {
23
39
  shared.src = share[1]
24
- foundSrc = true
25
- validate.shared(shared.src, cwd, errors)
40
+ foundSrcSetting = true
26
41
  continue
27
42
  }
28
43
  if (key === 'src' && !is.string(share[1])) {
@@ -32,11 +47,14 @@ module.exports = function configureShared ({ arc, pragmas, inventory, errors })
32
47
  }
33
48
  }
34
49
 
50
+ if (foundSrcSetting) validate.shared(shared.src, cwd, errors)
51
+ else shared.src = src
52
+
35
53
  // Exit if configured shared folder doesn't exist
36
54
  if (!is.exists(shared.src)) return null
37
55
 
38
56
  // Proceeding from here resets all shared config, so make sure it's only if specific shared are specified
39
- let some = arc.shared?.length && !(arc?.shared?.length === 1 && foundSrc)
57
+ let some = arc.shared?.length && !(arc?.shared?.length === 1 && foundSrcSetting)
40
58
  if (some) {
41
59
  // Reset shared settings
42
60
  for (let pragma of lambdas) {
@@ -1,10 +1,12 @@
1
+ let populate = require('./populate-other')
1
2
  let { asapSrc, is } = require('../../lib')
2
3
 
3
- module.exports = function configureStatic ({ arc, inventory }) {
4
+ module.exports = function configureStatic ({ arc, inventory, errors }) {
5
+ let staticSetters = inventory.plugins?._methods?.set?.static
4
6
  let httpSetters = inventory.plugins?._methods?.set?.http
5
7
 
6
8
  // @static is inferred by @http
7
- if (!arc.static && !arc.http && !httpSetters) return null
9
+ if (!arc.static && !staticSetters && !arc.http && !httpSetters) return null
8
10
 
9
11
  let staticPragma = arc.static || []
10
12
  let _static = {
@@ -24,6 +26,14 @@ module.exports = function configureStatic ({ arc, inventory }) {
24
26
  if (isDisabled) return false
25
27
  }
26
28
 
29
+ _static = populate.settings({
30
+ errors,
31
+ settings: _static,
32
+ plugins: staticSetters,
33
+ inventory,
34
+ type: 'static',
35
+ })
36
+
27
37
  let settings = Object.entries(_static).map(([ setting ]) => setting)
28
38
  for (let setting of staticPragma) {
29
39
  let validSetting = key => settings.includes(key)
@@ -1,52 +1,72 @@
1
- let { is } = require('../../lib')
1
+ let populate = require('./populate-other')
2
+ let { is, capitalize } = require('../../lib')
2
3
  let validate = require('./validate')
3
4
 
4
- module.exports = function configureTablesIndexes ({ arc, errors }) {
5
- if (!arc['tables-indexes'] || !arc['tables-indexes'].length) return null
6
- if (arc['tables-indexes'] && !arc.tables) {
5
+ module.exports = function configureTablesIndexes ({ arc, inventory, errors }) {
6
+ let $indexes = 'tables-indexes' // It's quite long!
7
+ let indexesSetters = inventory.plugins?._methods?.set?.[$indexes]
8
+ let tablesSetters = inventory.plugins?._methods?.set?.tables
9
+ if ((!arc[$indexes] || !arc[$indexes].length) && !indexesSetters) return null
10
+ if ((arc[$indexes] || indexesSetters) &&
11
+ (!arc.tables && !tablesSetters)) {
7
12
  errors.push(`Specifying @tables-indexes requires specifying corresponding @tables`)
8
13
  return null
9
14
  }
10
15
 
11
- let indexes = getIndexes(arc, 'tables-indexes', errors)
12
- validate.tablesIndexes(indexes, '@tables-indexes', errors)
16
+ let indexTemplate = () => ({
17
+ name: undefined,
18
+ partitionKey: null,
19
+ partitionKeyType: null,
20
+ sortKey: null,
21
+ sortKeyType: null,
22
+ indexName: null,
23
+ })
13
24
 
14
- return indexes
15
- }
25
+ let indexes = []
26
+ let plugins = populate.resources({
27
+ errors,
28
+ template: indexTemplate(),
29
+ plugins: indexesSetters,
30
+ inventory,
31
+ type: 'indexes',
32
+ valid: { name: 'string' }
33
+ })
34
+ if (plugins) indexes.push(...plugins)
16
35
 
17
- let getIndexes = (arc, pragma, errors) => {
18
- let isCustomName = key => is.string(key) && key.toLowerCase() === 'name'
19
- function error (item) { errors.push(`Invalid @${pragma} item: ${item}`) }
20
- return arc[pragma].map(index => {
36
+ let userland = arc?.[$indexes]?.map(index => {
21
37
  if (is.object(index)) {
22
- let name = Object.keys(index)[0]
23
- let partitionKey = null
24
- let partitionKeyType = null
25
- let sortKey = null
26
- let sortKeyType = null
27
- let indexName = null
28
- Object.entries(index[name]).forEach(([ key, value ]) => {
38
+ let i = indexTemplate()
39
+ i.name = Object.keys(index)[0]
40
+ Object.entries(index[i.name]).forEach(([ key, value ]) => {
29
41
  if (is.sortKey(value)) {
30
- sortKey = key
31
- sortKeyType = value.replace('**', '')
42
+ i.sortKey = key
43
+ i.sortKeyType = value.replace('**', '').toLowerCase()
44
+ if (!i.sortKeyType) i.sortKeyType = 'string'
32
45
  }
33
46
  else if (is.primaryKey(value)) {
34
- partitionKey = key
35
- partitionKeyType = value.replace('*', '')
47
+ i.partitionKey = key
48
+ i.partitionKeyType = value.replace('*', '').toLowerCase()
49
+ if (!i.partitionKeyType) i.partitionKeyType = 'string'
36
50
  }
37
- else if (isCustomName(key)) {
38
- indexName = value
51
+ else if (key?.toLowerCase() === 'name') {
52
+ i.indexName = value
39
53
  }
40
54
  })
41
- return {
42
- indexName,
43
- name,
44
- partitionKey,
45
- partitionKeyType,
46
- sortKey,
47
- sortKeyType,
48
- }
55
+ return i
49
56
  }
50
- error(index)
51
- }).filter(Boolean) // Invalid indexes may create undefined entries in the map
57
+ errors.push(`Invalid @${$indexes} item: ${index}`)
58
+ }).filter(Boolean) // Invalid indexes or plugins may create undefined entries in the map
59
+ if (userland) indexes.push(...userland)
60
+
61
+ // Normalize key type casing
62
+ if (indexes.length) indexes = indexes.map(index => {
63
+ let { sortKeyType, partitionKeyType } = index
64
+ if (sortKeyType) index.sortKeyType = capitalize(index.sortKeyType)
65
+ if (partitionKeyType) index.partitionKeyType = capitalize(index.partitionKeyType)
66
+ return index
67
+ })
68
+
69
+ validate.tablesIndexes(indexes, '@tables-indexes', errors)
70
+
71
+ return indexes
52
72
  }
@@ -1,54 +1,70 @@
1
- let { is } = require('../../lib')
1
+ let populate = require('./populate-other')
2
+ let { is, capitalize } = require('../../lib')
2
3
  let validate = require('./validate')
3
4
 
4
- module.exports = function configureTables ({ arc, errors }) {
5
- if (!arc.tables || !arc.tables.length) return null
5
+ module.exports = function configureTables ({ arc, inventory, errors }) {
6
+ let tablesSetters = inventory.plugins?._methods?.set?.tables
7
+ if ((!arc.tables || !arc.tables.length) && !tablesSetters) return null
6
8
 
7
9
  let pitrLong = 'PointInTimeRecovery' // It's just so long
10
+ let tableTemplate = () => ({
11
+ name: undefined,
12
+ partitionKey: null,
13
+ partitionKeyType: null,
14
+ sortKey: null,
15
+ sortKeyType: null,
16
+ stream: null,
17
+ ttl: null,
18
+ encrypt: null,
19
+ pitr: null,
20
+ })
8
21
 
9
- let tables = arc.tables.map(table => {
22
+ let tables = []
23
+ let plugins = populate.resources({
24
+ errors,
25
+ template: tableTemplate(),
26
+ plugins: tablesSetters,
27
+ inventory,
28
+ type: 'tables',
29
+ valid: { name: 'string' }
30
+ })
31
+ if (plugins) tables.push(...plugins)
32
+
33
+ let userland = arc?.tables?.map(table => {
10
34
  if (is.object(table)) {
11
- let name = Object.keys(table)[0]
12
- let partitionKey = null
13
- let partitionKeyType = null
14
- let sortKey = null
15
- let sortKeyType = null
16
- let stream = null
17
- let ttl = null
18
- let encrypt = null
19
- let pitr = null
20
- let pitrOld // Old opt, remove in some future breaking change
21
- Object.entries(table[name]).forEach(([ key, value ]) => {
35
+ let t = tableTemplate()
36
+ t.name = Object.keys(table)[0]
37
+ Object.entries(table[t.name]).forEach(([ key, value ]) => {
22
38
  if (is.sortKey(value)) {
23
- sortKey = key
24
- sortKeyType = value.replace('**', '')
39
+ t.sortKey = key
40
+ t.sortKeyType = value.replace('**', '').toLowerCase()
41
+ if (!t.sortKeyType) t.sortKeyType = 'string'
25
42
  }
26
43
  else if (is.primaryKey(value)) {
27
- partitionKey = key
28
- partitionKeyType = value.replace('*', '')
44
+ t.partitionKey = key
45
+ t.partitionKeyType = value.replace('*', '').toLowerCase()
46
+ if (!t.partitionKeyType) t.partitionKeyType = 'string'
29
47
  }
30
- if (key === 'stream') stream = value
31
- if (value === 'TTL') ttl = key
32
- if (key === 'encrypt') encrypt = value
33
- if (key === 'pitr') pitr = value
34
- if (key === pitrLong) pitrOld = value
48
+ if (key === 'stream') t.stream = value
49
+ if (value === 'TTL') t.ttl = key
50
+ if (key === 'encrypt') t.encrypt = value
51
+ if (key === 'PITR') t.pitr = value
52
+ if (key === 'pitr') t.pitr = value
53
+ if (key === pitrLong) t.PointInTimeRecovery = value
35
54
  })
36
- let t = {
37
- name,
38
- partitionKey,
39
- partitionKeyType,
40
- sortKey,
41
- sortKeyType,
42
- stream,
43
- ttl,
44
- encrypt,
45
- pitr,
46
- }
47
- if (pitrOld !== undefined) t.PointInTimeRecovery = pitrOld
48
55
  return t
49
56
  }
50
57
  errors.push(`Invalid @tables item: ${table}`)
51
- }).filter(Boolean) // Invalid tables may create undefined entries in the map
58
+ }).filter(Boolean) // Invalid tables or plugins may create undefined entries in the map
59
+ if (userland) tables.push(...userland)
60
+
61
+ // Normalize key type casing
62
+ if (tables.length) tables = tables.map(table => {
63
+ let { sortKeyType, partitionKeyType } = table
64
+ if (sortKeyType) table.sortKeyType = capitalize(table.sortKeyType)
65
+ if (partitionKeyType) table.partitionKeyType = capitalize(table.partitionKeyType)
66
+ return table
67
+ })
52
68
 
53
69
  validate.tables(tables, '@tables', errors)
54
70
 
@@ -2,7 +2,7 @@ let { join, resolve, sep } = require('path')
2
2
  let { is } = require('../../../lib')
3
3
 
4
4
  module.exports = function validateShared (src, cwd, errors) {
5
- let path = src && resolve(join(cwd, src))
5
+ let path = src && src.startsWith(cwd) ? src : resolve(join(cwd, src))
6
6
 
7
7
  if (!is.exists(path)) errors.push(`Directory not found: ${src}`)
8
8
  else if (!is.folder(path)) errors.push(`Must be a directory: ${src}`)
@@ -10,12 +10,13 @@ let { deepStrictEqual } = require('assert')
10
10
  module.exports = function validateTablesAndIndexes (pragma, pragmaName, errors) {
11
11
  if (pragma?.length) {
12
12
  pragma.forEach(table => {
13
- let { name, indexName, partitionKey, sortKey } = table
13
+ let { name, indexName, partitionKey, partitionKeyType, sortKey } = table
14
14
 
15
15
  size(name, 3, 255, pragmaName, errors)
16
16
  regex(name, 'veryLooseName', pragmaName, errors)
17
17
 
18
18
  if (!partitionKey) errors.push(`Invalid ${pragmaName} item (partition key required): '${name}'`)
19
+ if (!partitionKeyType) errors.push(`Invalid ${pragmaName} item (partition key type required): '${name}'`)
19
20
  if (indexName) {
20
21
  size(indexName, 3, 255, pragmaName, errors)
21
22
  regex(indexName, 'veryLooseName', pragmaName, errors)
@@ -1,31 +1,47 @@
1
1
  let { join } = require('path')
2
+ let populate = require('./populate-other')
2
3
  let validate = require('./validate')
3
4
  let { is } = require('../../lib')
4
5
 
5
6
  module.exports = function configureViews ({ arc, pragmas, inventory, errors }) {
6
- if (arc.views && !arc.http) {
7
+ let httpSetters = inventory.plugins?._methods?.set?.http
8
+ if (arc.views && (!arc.http && !httpSetters)) {
7
9
  errors.push('@views requires @http')
8
10
  return null
9
11
  }
10
- if (!arc.http) return null
12
+ if (!arc.http && !httpSetters) return null
11
13
 
12
14
  let { cwd, src: projSrc } = inventory._project
13
15
  let src = join(projSrc, 'views')
14
16
  let views = {
15
- src,
16
- views: [] // Revert to null later if none are defined
17
+ src: null,
18
+ views: []
19
+ }
20
+
21
+ let foundSrcSetting = false
22
+ let pluginSrc = populate.settings({
23
+ errors,
24
+ settings: views,
25
+ plugins: inventory.plugins?._methods?.set?.views,
26
+ inventory,
27
+ type: 'views',
28
+ valid: { src: 'string' },
29
+ })
30
+ // Views setters only support src, and do not support specifying Lambdas
31
+ // Lambda paths have not yet been reified in Inventory
32
+ if (is.string(pluginSrc?.src)) {
33
+ views.src = pluginSrc.src
34
+ foundSrcSetting = true
17
35
  }
18
36
 
19
37
  // First pass to get + check views folder (if any)
20
- let foundSrc = false
21
38
  if (arc?.views?.length) {
22
39
  for (let view of arc.views) {
23
40
  if (is.array(view)) {
24
41
  let key = view[0]?.toLowerCase()
25
42
  if (key === 'src' && is.string(view[1])) {
26
43
  views.src = view[1]
27
- foundSrc = true
28
- validate.shared(views.src, cwd, errors)
44
+ foundSrcSetting = true
29
45
  continue
30
46
  }
31
47
  if (key === 'src' && !is.string(view[1])) {
@@ -35,11 +51,14 @@ module.exports = function configureViews ({ arc, pragmas, inventory, errors }) {
35
51
  }
36
52
  }
37
53
 
54
+ if (foundSrcSetting) validate.shared(views.src, cwd, errors)
55
+ else views.src = src
56
+
38
57
  // Exit if default views folder doesn't exist
39
58
  if (!is.exists(views.src)) return null
40
59
 
41
60
  // Proceeding from here resets all views config, so make sure it's only if specific views are specified
42
- let some = arc.views?.length && !(arc?.views?.length === 1 && foundSrc)
61
+ let some = arc.views?.length && !(arc?.views?.length === 1 && foundSrcSetting)
43
62
  if (some) {
44
63
  // Reset views settings
45
64
  for (let route of pragmas.http) {
package/src/lib/index.js CHANGED
@@ -9,6 +9,9 @@ let pragmas = require('./pragmas')
9
9
  * Why take up a whole fs block when smol libs can just live here?
10
10
  */
11
11
 
12
+ // Capitalize a string (used to normalize table/index key types)
13
+ let capitalize = str => str[0].toUpperCase() + str.substr(1)
14
+
12
15
  // For setting `lambda.build`, compiled + transpiled are effectively the same
13
16
  let compiledRuntimes = [ 'compiled', 'transpiled' ]
14
17
 
@@ -29,6 +32,7 @@ let tidyError = err => err.message + `\n` + err.stack.split('\n').slice(1).join(
29
32
 
30
33
  module.exports = {
31
34
  asapSrc,
35
+ capitalize,
32
36
  compiledRuntimes,
33
37
  errorFmt,
34
38
  getLambdaDirs,
package/src/lib/is.js CHANGED
@@ -1,19 +1,32 @@
1
1
  let { existsSync, lstatSync } = require('fs')
2
2
 
3
+ // Types
4
+ let array = item => Array.isArray(item)
5
+ let bool = item => typeof item === 'boolean'
6
+ let defined = item => typeof item !== 'undefined'
7
+ let fn = item => typeof item === 'function'
8
+ let nullish = item => typeof item === 'undefined' || item === null
9
+ let number = item => Number.isInteger(item)
10
+ let object = item => typeof item === 'object' && !Array.isArray(item)
11
+ let string = item => typeof item === 'string'
12
+ // Filesystem
13
+ let exists = path => existsSync(path)
14
+ let folder = path => existsSync(path) && lstatSync(path).isDirectory()
15
+ // Pragma-specific stuff
16
+ let primaryKey = val => string(val) && [ '*', '*string', '*number' ].includes(val.toLowerCase())
17
+ let sortKey = val => string(val) && [ '**', '**string', '**number' ].includes(val.toLowerCase())
18
+
3
19
  module.exports = {
4
- // Types
5
- array: item => Array.isArray(item),
6
- bool: item => typeof item === 'boolean',
7
- defined: item => typeof item !== 'undefined',
8
- fn: item => typeof item === 'function',
9
- nullish: item => typeof item === 'undefined' || item === null,
10
- number: item => Number.isInteger(item),
11
- object: item => typeof item === 'object' && !Array.isArray(item),
12
- string: item => typeof item === 'string',
13
- // Filesystem
14
- exists: path => existsSync(path),
15
- folder: path => existsSync(path) && lstatSync(path).isDirectory(),
16
- // Pragma-specific stuff
17
- primaryKey: val => typeof val === 'string' && (val.startsWith('*String') || val.startsWith('*Number')),
18
- sortKey: val => typeof val === 'string' && (val.startsWith('**String') || val.startsWith('**Number')),
20
+ array,
21
+ bool,
22
+ defined,
23
+ fn,
24
+ nullish,
25
+ number,
26
+ object,
27
+ string,
28
+ exists,
29
+ folder,
30
+ primaryKey,
31
+ sortKey,
19
32
  }