Import other modules into monorepo
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
26d54033af
commit
9be9c44a32
12
package.json
12
package.json
@ -8,17 +8,25 @@
|
||||
"lib": "lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@extollo/di": "git+https://code.garrettmills.dev/extollo/di",
|
||||
"@extollo/util": "git+https://code.garrettmills.dev/extollo/util",
|
||||
"@types/busboy": "^0.2.3",
|
||||
"@types/mkdirp": "^1.0.1",
|
||||
"@types/negotiator": "^0.6.1",
|
||||
"@types/node": "^14.14.37",
|
||||
"@types/pg": "^8.6.0",
|
||||
"@types/pluralize": "^0.0.29",
|
||||
"@types/pug": "^2.0.4",
|
||||
"@types/rimraf": "^3.0.0",
|
||||
"@types/ssh2": "^0.5.46",
|
||||
"busboy": "^0.3.1",
|
||||
"colors": "^1.4.0",
|
||||
"dotenv": "^8.2.0",
|
||||
"mkdirp": "^1.0.4",
|
||||
"negotiator": "^0.6.2",
|
||||
"pg": "^8.6.0",
|
||||
"pluralize": "^8.0.0",
|
||||
"pug": "^3.0.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"ssh2": "^1.1.0",
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.2.3"
|
||||
},
|
||||
|
275
pnpm-lock.yaml
275
pnpm-lock.yaml
@ -1,15 +1,23 @@
|
||||
dependencies:
|
||||
'@extollo/di': code.garrettmills.dev/extollo/di/902c31997016d24c735448bbf1e58f06e94f4139
|
||||
'@extollo/util': code.garrettmills.dev/extollo/util/131e0c93ee7ea67d771053fbd69fbf7f6612fadf
|
||||
'@types/busboy': 0.2.3
|
||||
'@types/mkdirp': 1.0.1
|
||||
'@types/negotiator': 0.6.1
|
||||
'@types/node': 14.14.37
|
||||
'@types/pg': 8.6.0
|
||||
'@types/pluralize': 0.0.29
|
||||
'@types/pug': 2.0.4
|
||||
'@types/rimraf': 3.0.0
|
||||
'@types/ssh2': 0.5.46
|
||||
busboy: 0.3.1
|
||||
colors: 1.4.0
|
||||
dotenv: 8.2.0
|
||||
mkdirp: 1.0.4
|
||||
negotiator: 0.6.2
|
||||
pg: 8.6.0
|
||||
pluralize: 8.0.0
|
||||
pug: 3.0.2
|
||||
rimraf: 3.0.2
|
||||
ssh2: 1.1.0
|
||||
ts-node: 9.1.1_typescript@4.2.3
|
||||
typescript: 4.2.3
|
||||
lockfileVersion: 5.2
|
||||
@ -42,7 +50,7 @@ packages:
|
||||
/@types/glob/7.1.3:
|
||||
dependencies:
|
||||
'@types/minimatch': 3.0.4
|
||||
'@types/node': 14.14.37
|
||||
'@types/node': 14.17.1
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==
|
||||
@ -52,7 +60,7 @@ packages:
|
||||
integrity: sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==
|
||||
/@types/mkdirp/1.0.1:
|
||||
dependencies:
|
||||
'@types/node': 14.14.37
|
||||
'@types/node': 14.17.1
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-HkGSK7CGAXncr8Qn/0VqNtExEE+PHMWb+qlR1faHMao7ng6P3tAaoWWBMdva0gL5h4zprjIO89GJOLXsMcDm1Q==
|
||||
@ -64,6 +72,22 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw==
|
||||
/@types/node/14.17.1:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-/tpUyFD7meeooTRwl3sYlihx2BrJE7q9XF71EguPFIySj9B7qgnRtHsHTho+0AUm4m1SvWGm6uSncrR94q6Vtw==
|
||||
/@types/pg/8.6.0:
|
||||
dependencies:
|
||||
'@types/node': 14.17.1
|
||||
pg-protocol: 1.5.0
|
||||
pg-types: 2.2.0
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-3JXFrsl8COoqVB1+2Pqelx6soaiFVXzkT3fkuSNe7GB40ysfT0FHphZFPiqIXpMyTHSFRdLTyZzrFBrJRPAArA==
|
||||
/@types/pluralize/0.0.29:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-BYOID+l2Aco2nBik+iYS4SZX0Lf20KPILP5RGmM1IgzdwNdTs0eebiFriOPcej1sX9mLnSoiNte5zcFxssgpGA==
|
||||
/@types/pug/2.0.4:
|
||||
dev: false
|
||||
resolution:
|
||||
@ -71,27 +95,23 @@ packages:
|
||||
/@types/rimraf/3.0.0:
|
||||
dependencies:
|
||||
'@types/glob': 7.1.3
|
||||
'@types/node': 14.14.37
|
||||
'@types/node': 14.17.1
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-7WhJ0MdpFgYQPXlF4Dx+DhgvlPCfz/x5mHaeDQAKhcenvQP1KCpLQ18JklAqeGMYSAT2PxLpzd0g2/HE7fj7hQ==
|
||||
/@types/ssh2-streams/0.1.8:
|
||||
dependencies:
|
||||
'@types/node': 14.14.37
|
||||
'@types/node': 14.17.1
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-I7gixRPUvVIyJuCEvnmhr3KvA2dC0639kKswqD4H5b4/FOcnPtNU+qWLiXdKIqqX9twUvi5j0U1mwKE5CUsrfA==
|
||||
/@types/ssh2/0.5.46:
|
||||
dependencies:
|
||||
'@types/node': 14.14.37
|
||||
'@types/node': 14.17.1
|
||||
'@types/ssh2-streams': 0.1.8
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-1pC8FHrMPYdkLoUOwTYYifnSEPzAFZRsp3JFC/vokQ+dRrVI+hDBwz0SNmQ3pL6h39OSZlPs0uCG7wKJkftnaA==
|
||||
/@types/uuid/8.3.0:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==
|
||||
/acorn/7.4.1:
|
||||
dev: false
|
||||
engines:
|
||||
@ -125,10 +145,10 @@ packages:
|
||||
node: '>= 10.0.0'
|
||||
resolution:
|
||||
integrity: sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==
|
||||
/balanced-match/1.0.0:
|
||||
/balanced-match/1.0.2:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
/bcrypt-pbkdf/1.0.2:
|
||||
dependencies:
|
||||
tweetnacl: 0.14.5
|
||||
@ -137,7 +157,7 @@ packages:
|
||||
integrity: sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
|
||||
/brace-expansion/1.1.11:
|
||||
dependencies:
|
||||
balanced-match: 1.0.0
|
||||
balanced-match: 1.0.2
|
||||
concat-map: 0.0.1
|
||||
dev: false
|
||||
resolution:
|
||||
@ -146,6 +166,12 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
|
||||
/buffer-writer/2.0.0:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==
|
||||
/busboy/0.3.1:
|
||||
dependencies:
|
||||
dicer: 0.3.0
|
||||
@ -184,6 +210,16 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==
|
||||
/cpu-features/0.0.2:
|
||||
dependencies:
|
||||
nan: 2.14.2
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=8.0.0'
|
||||
optional: true
|
||||
requiresBuild: true
|
||||
resolution:
|
||||
integrity: sha512-/2yieBqvMcRj8McNzkycjW2v3OIUOibBfd2dLEJ0nWts8NobAxwiyw9phVNS6oDL8x8tz9F7uNVFEVpJncQpeA==
|
||||
/create-require/1.1.1:
|
||||
dev: false
|
||||
resolution:
|
||||
@ -228,7 +264,7 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==
|
||||
/glob/7.1.6:
|
||||
/glob/7.1.7:
|
||||
dependencies:
|
||||
fs.realpath: 1.0.0
|
||||
inflight: 1.0.6
|
||||
@ -238,7 +274,7 @@ packages:
|
||||
path-is-absolute: 1.0.1
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
||||
integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==
|
||||
/has-symbols/1.0.2:
|
||||
dev: false
|
||||
engines:
|
||||
@ -322,6 +358,11 @@ packages:
|
||||
hasBin: true
|
||||
resolution:
|
||||
integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||
/nan/2.14.2:
|
||||
dev: false
|
||||
optional: true
|
||||
resolution:
|
||||
integrity: sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
|
||||
/negotiator/0.6.2:
|
||||
dev: false
|
||||
engines:
|
||||
@ -340,6 +381,10 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||
/packet-reader/1.0.0:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==
|
||||
/path-is-absolute/1.0.1:
|
||||
dev: false
|
||||
engines:
|
||||
@ -350,6 +395,97 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
|
||||
/pg-connection-string/2.5.0:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==
|
||||
/pg-int8/1.0.1:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4.0.0'
|
||||
resolution:
|
||||
integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==
|
||||
/pg-pool/3.3.0_pg@8.6.0:
|
||||
dependencies:
|
||||
pg: 8.6.0
|
||||
dev: false
|
||||
peerDependencies:
|
||||
pg: '>=8.0'
|
||||
resolution:
|
||||
integrity: sha512-0O5huCql8/D6PIRFAlmccjphLYWC+JIzvUhSzXSpGaf+tjTZc4nn+Lr7mLXBbFJfvwbP0ywDv73EiaBsxn7zdg==
|
||||
/pg-protocol/1.5.0:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==
|
||||
/pg-types/2.2.0:
|
||||
dependencies:
|
||||
pg-int8: 1.0.1
|
||||
postgres-array: 2.0.0
|
||||
postgres-bytea: 1.0.0
|
||||
postgres-date: 1.0.7
|
||||
postgres-interval: 1.2.0
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==
|
||||
/pg/8.6.0:
|
||||
dependencies:
|
||||
buffer-writer: 2.0.0
|
||||
packet-reader: 1.0.0
|
||||
pg-connection-string: 2.5.0
|
||||
pg-pool: 3.3.0_pg@8.6.0
|
||||
pg-protocol: 1.5.0
|
||||
pg-types: 2.2.0
|
||||
pgpass: 1.0.4
|
||||
dev: false
|
||||
engines:
|
||||
node: '>= 8.0.0'
|
||||
peerDependencies:
|
||||
pg-native: '>=2.0.0'
|
||||
peerDependenciesMeta:
|
||||
pg-native:
|
||||
optional: true
|
||||
resolution:
|
||||
integrity: sha512-qNS9u61lqljTDFvmk/N66EeGq3n6Ujzj0FFyNMGQr6XuEv4tgNTXvJQTfJdcvGit5p5/DWPu+wj920hAJFI+QQ==
|
||||
/pgpass/1.0.4:
|
||||
dependencies:
|
||||
split2: 3.2.2
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==
|
||||
/pluralize/8.0.0:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==
|
||||
/postgres-array/2.0.0:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==
|
||||
/postgres-bytea/1.0.0:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=
|
||||
/postgres-date/1.0.7:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==
|
||||
/postgres-interval/1.2.0:
|
||||
dependencies:
|
||||
xtend: 4.0.2
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==
|
||||
/promise/7.3.1:
|
||||
dependencies:
|
||||
asap: 2.0.6
|
||||
@ -447,10 +583,16 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw==
|
||||
/reflect-metadata/0.1.13:
|
||||
/readable-stream/3.6.0:
|
||||
dependencies:
|
||||
inherits: 2.0.4
|
||||
string_decoder: 1.3.0
|
||||
util-deprecate: 1.0.2
|
||||
dev: false
|
||||
engines:
|
||||
node: '>= 6'
|
||||
resolution:
|
||||
integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==
|
||||
integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
|
||||
/resolve/1.20.0:
|
||||
dependencies:
|
||||
is-core-module: 2.2.0
|
||||
@ -460,11 +602,15 @@ packages:
|
||||
integrity: sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
|
||||
/rimraf/3.0.2:
|
||||
dependencies:
|
||||
glob: 7.1.6
|
||||
glob: 7.1.7
|
||||
dev: false
|
||||
hasBin: true
|
||||
resolution:
|
||||
integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
|
||||
/safe-buffer/5.2.1:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
/safer-buffer/2.1.2:
|
||||
dev: false
|
||||
resolution:
|
||||
@ -482,30 +628,37 @@ packages:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
/ssh2-streams/0.4.10:
|
||||
/split2/3.2.2:
|
||||
dependencies:
|
||||
readable-stream: 3.6.0
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==
|
||||
/ssh2/1.1.0:
|
||||
dependencies:
|
||||
asn1: 0.2.4
|
||||
bcrypt-pbkdf: 1.0.2
|
||||
streamsearch: 0.1.2
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=5.2.0'
|
||||
node: '>=10.16.0'
|
||||
optionalDependencies:
|
||||
cpu-features: 0.0.2
|
||||
nan: 2.14.2
|
||||
requiresBuild: true
|
||||
resolution:
|
||||
integrity: sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ==
|
||||
/ssh2/0.8.9:
|
||||
dependencies:
|
||||
ssh2-streams: 0.4.10
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=5.2.0'
|
||||
resolution:
|
||||
integrity: sha512-GmoNPxWDMkVpMFa9LVVzQZHF6EW3WKmBwL+4/GeILf2hFmix5Isxm7Amamo8o7bHiU0tC+wXsGcUXOxp8ChPaw==
|
||||
integrity: sha512-CidQLG2ZacoT0Z7O6dOyisj4JdrOrLVJ4KbHjVNz9yI1vO08FAYQPcnkXY9BP8zeYo+J/nBgY6Gg4R7w4WFWtg==
|
||||
/streamsearch/0.1.2:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.8.0'
|
||||
resolution:
|
||||
integrity: sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=
|
||||
/string_decoder/1.3.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
|
||||
/to-fast-properties/2.0.0:
|
||||
dev: false
|
||||
engines:
|
||||
@ -544,11 +697,10 @@ packages:
|
||||
hasBin: true
|
||||
resolution:
|
||||
integrity: sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==
|
||||
/uuid/8.3.2:
|
||||
/util-deprecate/1.0.2:
|
||||
dev: false
|
||||
hasBin: true
|
||||
resolution:
|
||||
integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||
integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
/void-elements/3.1.0:
|
||||
dev: false
|
||||
engines:
|
||||
@ -570,60 +722,37 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
/xtend/4.0.2:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.4'
|
||||
resolution:
|
||||
integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
||||
/yn/3.1.1:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=6'
|
||||
resolution:
|
||||
integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
|
||||
code.garrettmills.dev/extollo/di/902c31997016d24c735448bbf1e58f06e94f4139:
|
||||
dependencies:
|
||||
'@extollo/util': code.garrettmills.dev/extollo/util/131e0c93ee7ea67d771053fbd69fbf7f6612fadf
|
||||
'@types/node': 14.14.37
|
||||
reflect-metadata: 0.1.13
|
||||
typescript: 4.2.3
|
||||
dev: false
|
||||
name: '@extollo/di'
|
||||
prepare: true
|
||||
requiresBuild: true
|
||||
resolution:
|
||||
commit: 902c31997016d24c735448bbf1e58f06e94f4139
|
||||
repo: https://code.garrettmills.dev/extollo/di
|
||||
type: git
|
||||
version: 0.4.4
|
||||
code.garrettmills.dev/extollo/util/131e0c93ee7ea67d771053fbd69fbf7f6612fadf:
|
||||
dependencies:
|
||||
'@types/mkdirp': 1.0.1
|
||||
'@types/node': 14.14.37
|
||||
'@types/rimraf': 3.0.0
|
||||
'@types/ssh2': 0.5.46
|
||||
'@types/uuid': 8.3.0
|
||||
colors: 1.4.0
|
||||
mkdirp: 1.0.4
|
||||
rimraf: 3.0.2
|
||||
ssh2: 0.8.9
|
||||
typescript: 4.2.3
|
||||
uuid: 8.3.2
|
||||
dev: false
|
||||
name: '@extollo/util'
|
||||
prepare: true
|
||||
requiresBuild: true
|
||||
resolution:
|
||||
commit: 131e0c93ee7ea67d771053fbd69fbf7f6612fadf
|
||||
repo: https://code.garrettmills.dev/extollo/util
|
||||
type: git
|
||||
version: 0.3.2
|
||||
specifiers:
|
||||
'@extollo/di': git+https://code.garrettmills.dev/extollo/di
|
||||
'@extollo/util': git+https://code.garrettmills.dev/extollo/util
|
||||
'@types/busboy': ^0.2.3
|
||||
'@types/mkdirp': ^1.0.1
|
||||
'@types/negotiator': ^0.6.1
|
||||
'@types/node': ^14.14.37
|
||||
'@types/pg': ^8.6.0
|
||||
'@types/pluralize': ^0.0.29
|
||||
'@types/pug': ^2.0.4
|
||||
'@types/rimraf': ^3.0.0
|
||||
'@types/ssh2': ^0.5.46
|
||||
busboy: ^0.3.1
|
||||
colors: ^1.4.0
|
||||
dotenv: ^8.2.0
|
||||
mkdirp: ^1.0.4
|
||||
negotiator: ^0.6.2
|
||||
pg: ^8.6.0
|
||||
pluralize: ^8.0.0
|
||||
pug: ^3.0.2
|
||||
rimraf: ^3.0.2
|
||||
ssh2: ^1.1.0
|
||||
ts-node: ^9.1.1
|
||||
typescript: ^4.2.3
|
||||
|
443
src/cli/Directive.ts
Normal file
443
src/cli/Directive.ts
Normal file
@ -0,0 +1,443 @@
|
||||
import {Injectable, Inject} from "../di"
|
||||
import {infer, ErrorWithContext} from "../util"
|
||||
import {CLIOption} from "./directive/options/CLIOption"
|
||||
import {PositionalOption} from "./directive/options/PositionalOption";
|
||||
import {FlagOption} from "./directive/options/FlagOption";
|
||||
import {AppClass} from "../lifecycle/AppClass";
|
||||
import {Logging} from "../service/Logging";
|
||||
|
||||
/**
|
||||
* Type alias for a definition of a command-line option.
|
||||
*
|
||||
* This can be either an instance of CLIOption or a string describing an option.
|
||||
*
|
||||
* @example
|
||||
* Some examples of positional/flag options defined by strings:
|
||||
* `'{file name} | canonical name of the resource to create'`
|
||||
*
|
||||
* `'--push -p {value} | the value to be pushed'`
|
||||
*
|
||||
* `'--force -f | do a force push'`
|
||||
*/
|
||||
export type OptionDefinition = CLIOption<any> | string
|
||||
|
||||
/**
|
||||
* An error thrown when an invalid option was detected.
|
||||
*/
|
||||
export class OptionValidationError extends ErrorWithContext {}
|
||||
|
||||
/**
|
||||
* A base class representing a sub-command in the command-line utility.
|
||||
*/
|
||||
@Injectable()
|
||||
export abstract class Directive extends AppClass {
|
||||
@Inject()
|
||||
protected readonly logging!: Logging
|
||||
|
||||
/** Parsed option values. */
|
||||
private _optionValues: any
|
||||
|
||||
/**
|
||||
* Get the keyword or array of keywords that will specify this directive.
|
||||
*
|
||||
* @example
|
||||
* If this returns `['up', 'start']`, the directive can be run by either of:
|
||||
*
|
||||
* ```shell
|
||||
* ./ex up
|
||||
* ./ex start
|
||||
* ```
|
||||
*/
|
||||
public abstract getKeywords(): string | string[]
|
||||
|
||||
/**
|
||||
* Get the usage description of this directive. Should be brief (1 sentence).
|
||||
*/
|
||||
public abstract getDescription(): string
|
||||
|
||||
/**
|
||||
* Optionally, specify a longer usage text that is shown on the directive's `--help` page.
|
||||
*/
|
||||
public getHelpText(): string {
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of options defined for this command.
|
||||
*/
|
||||
public getOptions(): OptionDefinition[] {
|
||||
return []
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the directive is run from the command line.
|
||||
*
|
||||
* The raw arguments are provided as `argv`, but you are encouraged to use
|
||||
* `getOptions()` and `option()` helpers to access the parsed options instead.
|
||||
*
|
||||
* @param argv
|
||||
*/
|
||||
public abstract handle(argv: string[]): void | Promise<void>
|
||||
|
||||
/**
|
||||
* Sets the parsed option values.
|
||||
* @param optionValues
|
||||
* @private
|
||||
*/
|
||||
private _setOptionValues(optionValues: any) {
|
||||
this._optionValues = optionValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a parsed option. If none exists, return `defaultValue`.
|
||||
* @param name
|
||||
* @param defaultValue
|
||||
*/
|
||||
public option(name: string, defaultValue?: any) {
|
||||
if ( name in this._optionValues ) {
|
||||
return this._optionValues[name]
|
||||
}
|
||||
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke this directive with the specified arguments.
|
||||
*
|
||||
* If usage was requested (see `didRequestUsage()`), it prints the extended usage info.
|
||||
*
|
||||
* Otherwise, it parses the options from `argv` and calls `handle()`.
|
||||
*
|
||||
* @param argv
|
||||
*/
|
||||
async invoke(argv: string[]) {
|
||||
const options = this.getResolvedOptions()
|
||||
|
||||
if ( this.didRequestUsage(argv) ) {
|
||||
// @ts-ignore
|
||||
const positionalArguments: PositionalOption<any>[] = options.filter(opt => opt instanceof PositionalOption)
|
||||
|
||||
// @ts-ignore
|
||||
const flagArguments: FlagOption<any>[] = options.filter(opt => opt instanceof FlagOption)
|
||||
|
||||
const positionalDisplay: string = positionalArguments.map(x => `<${x.getArgumentName()}>`).join(' ')
|
||||
const flagDisplay: string = flagArguments.length ? ' [...flags]' : ''
|
||||
|
||||
console.log([
|
||||
'',
|
||||
`DIRECTIVE: ${this.getMainKeyword()} - ${this.getDescription()}`,
|
||||
'',
|
||||
`USAGE: ${this.getMainKeyword()} ${positionalDisplay}${flagDisplay}`,
|
||||
].join('\n'))
|
||||
|
||||
if ( positionalArguments.length ) {
|
||||
console.log([
|
||||
'',
|
||||
`POSITIONAL ARGUMENTS:`,
|
||||
...(positionalArguments.map(arg => {
|
||||
return ` ${arg.getArgumentName()}${arg.message ? ' - ' + arg.message : ''}`
|
||||
})),
|
||||
].join('\n'))
|
||||
}
|
||||
|
||||
if ( flagArguments.length ) {
|
||||
console.log([
|
||||
'',
|
||||
`FLAGS:`,
|
||||
...(flagArguments.map(arg => {
|
||||
return ` ${arg.shortFlag ? arg.shortFlag + ', ' : ''}${arg.longFlag}${arg.argumentDescription ? ' {' + arg.argumentDescription + '}' : ''}${arg.message ? ' - ' + arg.message : ''}`
|
||||
})),
|
||||
].join('\n'))
|
||||
}
|
||||
|
||||
const help = this.getHelpText()
|
||||
if ( help ) {
|
||||
console.log('\n' + help)
|
||||
}
|
||||
|
||||
console.log('\n')
|
||||
} else {
|
||||
try {
|
||||
const optionValues = this.parseOptions(options, argv)
|
||||
this._setOptionValues(optionValues)
|
||||
await this.handle(argv)
|
||||
} catch (e) {
|
||||
console.error(e.message)
|
||||
if ( e instanceof OptionValidationError ) {
|
||||
// expecting, value, requirements
|
||||
if ( e.context.expecting ) {
|
||||
console.error(` - Expecting: ${e.context.expecting}`)
|
||||
}
|
||||
|
||||
if ( e.context.requirements && Array.isArray(e.context.requirements) ) {
|
||||
for ( const req of e.context.requirements ) {
|
||||
console.error(` - ${req}`)
|
||||
}
|
||||
}
|
||||
|
||||
if ( e.context.value ) {
|
||||
console.error(` - ${e.context.value}`)
|
||||
}
|
||||
}
|
||||
console.error('\nUse --help for more info.')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the array of option definitions to CLIOption instances.
|
||||
* Of note, this resolves the string-form definitions to actual CLIOption instances.
|
||||
*/
|
||||
public getResolvedOptions(): CLIOption<any>[] {
|
||||
return this.getOptions().map(option => {
|
||||
if ( typeof option === 'string' ) {
|
||||
return this.instantiateOptionFromString(option)
|
||||
} else {
|
||||
return option
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main keyword displayed for this directive.
|
||||
* @example
|
||||
* If `getKeywords()` returns `['up', 'start']`, this will return `'up'`.
|
||||
*/
|
||||
public getMainKeyword(): string {
|
||||
const kws = this.getKeywords()
|
||||
|
||||
if ( Array.isArray(kws) ) {
|
||||
return kws[0]
|
||||
}
|
||||
|
||||
return kws
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given keyword should invoke this directive.
|
||||
* @param name
|
||||
*/
|
||||
public matchesKeyword(name: string) {
|
||||
let kws = this.getKeywords()
|
||||
if ( !Array.isArray(kws) ) kws = [kws]
|
||||
return kws.includes(name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the given output to the log as success text.
|
||||
* @param output
|
||||
*/
|
||||
success(output: any) {
|
||||
this.logging.success(output, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the given output to the log as error text.
|
||||
* @param output
|
||||
*/
|
||||
error(output: any) {
|
||||
this.logging.error(output, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the given output to the log as warning text.
|
||||
* @param output
|
||||
*/
|
||||
warn(output: any) {
|
||||
this.logging.warn(output, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the given output to the log as info text.
|
||||
* @param output
|
||||
*/
|
||||
info(output: any) {
|
||||
this.logging.info(output, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the given output to the log as debugging text.
|
||||
* @param output
|
||||
*/
|
||||
debug(output: any) {
|
||||
this.logging.debug(output, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the given output to the log as verbose text.
|
||||
* @param output
|
||||
*/
|
||||
verbose(output: any) {
|
||||
this.logging.verbose(output, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the flag option that signals help. Usually, this is named 'help'
|
||||
* and supports the flags '--help' and '-?'.
|
||||
*/
|
||||
getHelpOption() {
|
||||
return new FlagOption('--help', '-?', 'usage information about this directive')
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the raw CLI arguments using an array of option class instances to build
|
||||
* a mapping of option names to provided values.
|
||||
*/
|
||||
parseOptions(options: CLIOption<any>[], args: string[]) {
|
||||
// @ts-ignore
|
||||
let positionalArguments: PositionalOption<any>[] = options.filter(cls => cls instanceof PositionalOption)
|
||||
|
||||
// @ts-ignore
|
||||
const flagArguments: FlagOption<any>[] = options.filter(cls => cls instanceof FlagOption)
|
||||
const optionValue: any = {}
|
||||
|
||||
flagArguments.push(this.getHelpOption())
|
||||
|
||||
let expectingFlagArgument = false
|
||||
let positionalFlagName = ''
|
||||
for ( const value of args ) {
|
||||
if ( value.startsWith('--') ) {
|
||||
if ( expectingFlagArgument ) {
|
||||
throw new OptionValidationError(`Unexpected flag argument. Expecting argument for flag: ${positionalFlagName}`, {
|
||||
expecting: positionalFlagName,
|
||||
})
|
||||
} else {
|
||||
const flagArgument = flagArguments.filter(x => x.longFlag === value)
|
||||
if ( flagArgument.length < 1 ) {
|
||||
throw new OptionValidationError(`Unknown flag argument: ${value}`, {
|
||||
value,
|
||||
})
|
||||
} else {
|
||||
if ( flagArgument[0].argumentDescription ) {
|
||||
positionalFlagName = flagArgument[0].getArgumentName()
|
||||
expectingFlagArgument = true
|
||||
} else {
|
||||
optionValue[flagArgument[0].getArgumentName()] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ( value.startsWith('-') ) {
|
||||
if ( expectingFlagArgument ) {
|
||||
throw new OptionValidationError(`Unknown flag argument: ${value}`, {
|
||||
expecting: positionalFlagName,
|
||||
})
|
||||
} else {
|
||||
const flagArgument = flagArguments.filter(x => x.shortFlag === value)
|
||||
if ( flagArgument.length < 1 ) {
|
||||
throw new OptionValidationError(`Unknown flag argument: ${value}`, {
|
||||
value
|
||||
})
|
||||
} else {
|
||||
if ( flagArgument[0].argumentDescription ) {
|
||||
positionalFlagName = flagArgument[0].getArgumentName()
|
||||
expectingFlagArgument = true
|
||||
} else {
|
||||
optionValue[flagArgument[0].getArgumentName()] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ( expectingFlagArgument ) {
|
||||
const inferredValue = infer(value)
|
||||
const optionInstance = flagArguments.filter(x => x.getArgumentName() === positionalFlagName)[0]
|
||||
if ( !optionInstance.validate(inferredValue) ) {
|
||||
throw new OptionValidationError(`Invalid value for argument: ${positionalFlagName}`, {
|
||||
requirements: optionInstance.getRequirementDisplays(),
|
||||
})
|
||||
}
|
||||
|
||||
optionValue[positionalFlagName] = inferredValue
|
||||
expectingFlagArgument = false
|
||||
} else {
|
||||
if ( positionalArguments.length < 1 ) {
|
||||
throw new OptionValidationError(`Unknown positional argument: ${value}`, {
|
||||
value
|
||||
})
|
||||
} else {
|
||||
const inferredValue = infer(value)
|
||||
if ( !positionalArguments[0].validate(inferredValue) ) {
|
||||
throw new OptionValidationError(`Invalid value for argument: ${positionalArguments[0].getArgumentName()}`, {
|
||||
requirements: positionalArguments[0].getRequirementDisplays(),
|
||||
})
|
||||
}
|
||||
|
||||
optionValue[positionalArguments[0].getArgumentName()] = infer(value)
|
||||
positionalArguments = positionalArguments.slice(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( expectingFlagArgument ) {
|
||||
throw new OptionValidationError(`Missing argument for flag: ${positionalFlagName}`, {
|
||||
expecting: positionalFlagName
|
||||
})
|
||||
}
|
||||
|
||||
if ( positionalArguments.length > 0 ) {
|
||||
throw new OptionValidationError(`Missing required argument: ${positionalArguments[0].getArgumentName()}`, {
|
||||
expecting: positionalArguments[0].getArgumentName()
|
||||
})
|
||||
}
|
||||
|
||||
return optionValue
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of CLIOption based on a string definition of a particular format.
|
||||
*
|
||||
* e.g. '{file name} | canonical name of the resource to create'
|
||||
* e.g. '--push -p {value} | the value to be pushed'
|
||||
* e.g. '--force -f | do a force push'
|
||||
*
|
||||
* @param string
|
||||
*/
|
||||
protected instantiateOptionFromString(string: string): CLIOption<any> {
|
||||
if ( string.startsWith('{') ) {
|
||||
// The string is a positional argument
|
||||
const stringParts = string.split('|').map(x => x.trim())
|
||||
const name = stringParts[0].replace(/\{|\}/g, '')
|
||||
return stringParts.length > 1 ? (new PositionalOption(name, stringParts[1])) : (new PositionalOption(name))
|
||||
} else {
|
||||
// The string is a flag argument
|
||||
const stringParts = string.split('|').map(x => x.trim())
|
||||
|
||||
// Parse the flag parts first
|
||||
const hasArgument = stringParts[0].indexOf('{') >= 0
|
||||
const flagString = hasArgument ? stringParts[0].substr(0, stringParts[0].indexOf('{')).trim() : stringParts[0].trim()
|
||||
const flagParts = flagString.split(' ')
|
||||
|
||||
let longFlag = flagParts[0].startsWith('--') ? flagParts[0] : undefined
|
||||
if ( !longFlag && flagParts.length > 1 ) {
|
||||
if ( flagParts[1].startsWith('--') ) {
|
||||
longFlag = flagParts[1]
|
||||
}
|
||||
}
|
||||
|
||||
let shortFlag = flagParts[0].length === 2 ? flagParts[0] : undefined
|
||||
if ( !shortFlag && flagParts.length > 1 ) {
|
||||
if ( flagParts[1].length === 2 ) {
|
||||
shortFlag = flagParts[1]
|
||||
}
|
||||
}
|
||||
|
||||
const argumentDescription = hasArgument ? stringParts[0].substring(stringParts[0].indexOf('{')+1, stringParts[0].indexOf('}')) : undefined
|
||||
const description = stringParts.length > 1 ? stringParts[1] : undefined
|
||||
|
||||
return new FlagOption(longFlag, shortFlag, description, argumentDescription)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if, at any point in the arguments, the help option's short or long flag appears.
|
||||
* @returns {boolean} - true if the help flag appeared
|
||||
*/
|
||||
didRequestUsage(argv: string[]) {
|
||||
const help_option = this.getHelpOption()
|
||||
for ( const arg of argv ) {
|
||||
if ( arg.trim() === help_option.longFlag || arg.trim() === help_option.shortFlag ) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
64
src/cli/Template.ts
Normal file
64
src/cli/Template.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import {UniversalPath} from '../util'
|
||||
|
||||
/**
|
||||
* Interface defining a template that can be generated using the TemplateDirective.
|
||||
*/
|
||||
export interface Template {
|
||||
/**
|
||||
* The name of the template as it will be specified from the command line.
|
||||
*
|
||||
* @example
|
||||
* If this is `'mytemplate'`, then the template will be created with:
|
||||
*
|
||||
* ```shell
|
||||
* ./ex new mytemplate some:path
|
||||
* ```
|
||||
*/
|
||||
name: string,
|
||||
|
||||
/**
|
||||
* The suffix of the file generated by this template.
|
||||
* @example `.mytemplate.ts`
|
||||
* @example `.controller.ts`
|
||||
*/
|
||||
fileSuffix: string,
|
||||
|
||||
/**
|
||||
* Brief description of the template displayed on the --help page for the TemplateDirective.
|
||||
* Should be brief (1 sentence).
|
||||
*/
|
||||
description: string,
|
||||
|
||||
/**
|
||||
* Array of path-strings that are resolved relative to the base `app` directory.
|
||||
* @example `['http', 'controllers']`
|
||||
* @example `['units']`
|
||||
*/
|
||||
baseAppPath: string[],
|
||||
|
||||
/**
|
||||
* Render the given template to a string which will be written to the file.
|
||||
* Note: this method should NOT write the contents to `targetFilePath`.
|
||||
*
|
||||
* @example
|
||||
* If the user enters:
|
||||
*
|
||||
* ```shell
|
||||
* ./ex new mytemplate path:to:NewInstance
|
||||
* ```
|
||||
*
|
||||
* Then, the following params are:
|
||||
* ```typescript
|
||||
* {
|
||||
* name: 'NewInstance',
|
||||
* fullCanonicalPath: 'path:to:NewInstance',
|
||||
* targetFilePath: UniversalPath { }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param name - the singular name of the resource
|
||||
* @param fullCanonicalName - the full canonical name of the resource
|
||||
* @param targetFilePath - the UniversalPath where the file will be written
|
||||
*/
|
||||
render: (name: string, fullCanonicalName: string, targetFilePath: UniversalPath) => string | Promise<string>
|
||||
}
|
31
src/cli/directive/RunDirective.ts
Normal file
31
src/cli/directive/RunDirective.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import {Directive} from "../Directive"
|
||||
import {CommandLineApplication} from "../service"
|
||||
import {Injectable} from "../../di"
|
||||
import {ErrorWithContext} from "../../util"
|
||||
import {Unit} from "../../lifecycle/Unit";
|
||||
|
||||
/**
|
||||
* A directive that starts the framework's final target normally.
|
||||
* In most cases, this runs the HTTP server, which would have been replaced
|
||||
* by the CommandLineApplication unit.
|
||||
*/
|
||||
@Injectable()
|
||||
export class RunDirective extends Directive {
|
||||
getDescription(): string {
|
||||
return 'run the application normally'
|
||||
}
|
||||
|
||||
getKeywords(): string | string[] {
|
||||
return ['run', 'up']
|
||||
}
|
||||
|
||||
async handle(): Promise<void> {
|
||||
if ( !CommandLineApplication.getReplacement() ) {
|
||||
throw new ErrorWithContext(`Cannot run application: no run target specified.`)
|
||||
}
|
||||
|
||||
const unit = <Unit> this.make(CommandLineApplication.getReplacement())
|
||||
await this.app().startUnit(unit)
|
||||
await this.app().stopUnit(unit)
|
||||
}
|
||||
}
|
47
src/cli/directive/ShellDirective.ts
Normal file
47
src/cli/directive/ShellDirective.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import {Directive} from "../Directive"
|
||||
import * as colors from "colors/safe"
|
||||
import * as repl from 'repl'
|
||||
import {DependencyKey} from "../../di";
|
||||
|
||||
/**
|
||||
* Launch an interactive REPL shell from within the application.
|
||||
* This is very useful for debugging and testing things during development.
|
||||
*/
|
||||
export class ShellDirective extends Directive {
|
||||
protected options: any = {
|
||||
welcome: `powered by Extollo, © ${(new Date).getFullYear()} Garrett Mills\nAccess your application using the "app" global.`,
|
||||
prompt: `${colors.blue('(')}extollo${colors.blue(') ➤ ')}`,
|
||||
}
|
||||
|
||||
/**
|
||||
* The created Node.js REPL server.
|
||||
* @protected
|
||||
*/
|
||||
protected repl?: repl.REPLServer
|
||||
|
||||
getDescription(): string {
|
||||
return 'launch an interactive shell inside your application'
|
||||
}
|
||||
|
||||
getKeywords(): string | string[] {
|
||||
return ['shell']
|
||||
}
|
||||
|
||||
getHelpText(): string {
|
||||
return ''
|
||||
}
|
||||
|
||||
async handle(): Promise<void> {
|
||||
const state: any = {
|
||||
app: this.app(),
|
||||
make: (target: DependencyKey, ...parameters: any[]) => this.make(target, ...parameters),
|
||||
}
|
||||
|
||||
await new Promise<void>(res => {
|
||||
console.log(this.options.welcome)
|
||||
this.repl = repl.start(this.options.prompt)
|
||||
Object.assign(this.repl.context, state)
|
||||
this.repl.on('exit', () => res())
|
||||
})
|
||||
}
|
||||
}
|
90
src/cli/directive/TemplateDirective.ts
Normal file
90
src/cli/directive/TemplateDirective.ts
Normal file
@ -0,0 +1,90 @@
|
||||
import {Directive, OptionDefinition} from "../Directive"
|
||||
import {PositionalOption} from "./options/PositionalOption"
|
||||
import {CommandLine} from "../service"
|
||||
import {Inject, Injectable} from "../../di";
|
||||
import {ErrorWithContext} from "../../util";
|
||||
|
||||
/**
|
||||
* Create a new file based on a template registered with the CommandLine service.
|
||||
*/
|
||||
@Injectable()
|
||||
export class TemplateDirective extends Directive {
|
||||
@Inject()
|
||||
protected readonly cli!: CommandLine
|
||||
|
||||
getKeywords(): string | string[] {
|
||||
return ['new', 'make']
|
||||
}
|
||||
|
||||
getDescription(): string {
|
||||
return 'create a new file from a registered template'
|
||||
}
|
||||
|
||||
getOptions(): OptionDefinition[] {
|
||||
const registeredTemplates = this.cli.getTemplates()
|
||||
const template = new PositionalOption('template_name', 'the template to base the new file on (e.g. model, controller)')
|
||||
template.whitelist(...registeredTemplates.pluck('name').all())
|
||||
|
||||
const destination = new PositionalOption('file_name', 'canonical name of the file to create (e.g. auth:Group, dash:Activity)')
|
||||
|
||||
return [template, destination]
|
||||
}
|
||||
|
||||
getHelpText(): string {
|
||||
const registeredTemplates = this.cli.getTemplates()
|
||||
|
||||
return [
|
||||
'Modules in Extollo register templates that can be used to quickly create common file types.',
|
||||
'',
|
||||
'For example, you can create a new model from @extollo/orm using the "model" template:',
|
||||
'',
|
||||
'./ex new model auth:Group',
|
||||
'',
|
||||
'This would create a new Group model in the ./src/app/models/auth/Group.model.ts file.',
|
||||
'',
|
||||
'AVAILABLE TEMPLATES:',
|
||||
'',
|
||||
...(registeredTemplates.map(template => {
|
||||
return ` - ${template.name}: ${template.description}`
|
||||
}).all())
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
async handle(argv: string[]) {
|
||||
const templateName: string = this.option('template_name')
|
||||
const destinationName: string = this.option('file_name')
|
||||
|
||||
if ( destinationName.includes('/') || destinationName.includes('\\') ) {
|
||||
this.error(`The destination should be a canonical name, not a file path.`)
|
||||
this.error(`Reference sub-directories using the : character instead.`)
|
||||
this.error(`Did you mean ${destinationName.replace(/\/|\\/g, ':')}?`)
|
||||
process.exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
const template = this.cli.getTemplate(templateName)
|
||||
if ( !template ) {
|
||||
throw new ErrorWithContext(`Unable to find template supposedly registered with name: ${templateName}`, {
|
||||
templateName,
|
||||
destinationName,
|
||||
})
|
||||
}
|
||||
|
||||
const name = destinationName.split(':').reverse()[0]
|
||||
const path = this.app().path('..', 'src', 'app', ...template.baseAppPath, ...(`${destinationName}${template.fileSuffix}`).split(':'))
|
||||
|
||||
if ( await path.exists() ) {
|
||||
this.error(`File already exists: ${path}`)
|
||||
process.exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
// Make sure the parent direction exists
|
||||
await path.concat('..').mkdir()
|
||||
|
||||
const contents = await template.render(name, destinationName, path.clone())
|
||||
await path.write(contents)
|
||||
|
||||
this.success(`Created new ${template.name} in ${path}`)
|
||||
}
|
||||
}
|
54
src/cli/directive/UsageDirective.ts
Normal file
54
src/cli/directive/UsageDirective.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import {Directive} from "../Directive"
|
||||
import {Injectable, Inject} from "../../di"
|
||||
import {padRight} from "../../util"
|
||||
import {CommandLine} from "../service"
|
||||
|
||||
/**
|
||||
* Directive that prints the help message and usage information about
|
||||
* directives registered with the command line utility.
|
||||
*/
|
||||
@Injectable()
|
||||
export class UsageDirective extends Directive {
|
||||
@Inject()
|
||||
protected readonly cli!: CommandLine
|
||||
|
||||
public getKeywords(): string | string[] {
|
||||
return 'help'
|
||||
}
|
||||
|
||||
public getDescription(): string {
|
||||
return 'print information about available commands'
|
||||
}
|
||||
|
||||
public handle(argv: string[]): void | Promise<void> {
|
||||
const directiveStrings = this.cli.getDirectives()
|
||||
.map(cls => this.make<Directive>(cls))
|
||||
.map<[string, string]>(dir => {
|
||||
return [dir.getMainKeyword(), dir.getDescription()]
|
||||
})
|
||||
|
||||
const maxLen = directiveStrings.max<number>(x => x[0].length)
|
||||
|
||||
const printStrings = directiveStrings.map(grp => {
|
||||
return [padRight(grp[0], maxLen + 1), grp[1]]
|
||||
})
|
||||
.map(grp => {
|
||||
return ` ${grp[0]}: ${grp[1]}`
|
||||
})
|
||||
.toArray()
|
||||
|
||||
console.log(this.cli.getASCIILogo())
|
||||
console.log([
|
||||
'',
|
||||
'Welcome to Extollo! Specify a command to get started.',
|
||||
'',
|
||||
`USAGE: ex <directive> [..options]`,
|
||||
'',
|
||||
...printStrings,
|
||||
'',
|
||||
'For usage information about a particular command, pass the --help flag.',
|
||||
'-------------------------------------------',
|
||||
`powered by Extollo, © ${(new Date).getFullYear()} Garrett Mills`,
|
||||
].join('\n'))
|
||||
}
|
||||
}
|
244
src/cli/directive/options/CLIOption.ts
Normal file
244
src/cli/directive/options/CLIOption.ts
Normal file
@ -0,0 +1,244 @@
|
||||
/**
|
||||
* A CLI option. Supports basic comparative, and set-based validation.
|
||||
* @class
|
||||
*/
|
||||
export abstract class CLIOption<T> {
|
||||
|
||||
/**
|
||||
* Do we use the whitelist?
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
protected _useWhitelist: boolean = false
|
||||
|
||||
/**
|
||||
* Do we use the blacklist?
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
protected _useBlacklist: boolean = false
|
||||
|
||||
/**
|
||||
* Do we use the less-than comparison?
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
protected _useLessThan: boolean = false
|
||||
|
||||
/**
|
||||
* Do we use the greater-than comparison?
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
protected _useGreaterThan: boolean = false
|
||||
|
||||
/**
|
||||
* Do we use the equality operator?
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
protected _useEquality: boolean = false
|
||||
|
||||
/**
|
||||
* Is this option optional?
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
protected _optional: boolean = false
|
||||
|
||||
/**
|
||||
* Whitelisted values.
|
||||
* @type {Array<*>}
|
||||
* @private
|
||||
*/
|
||||
protected _whitelist: T[] = []
|
||||
|
||||
/**
|
||||
* Blacklisted values.
|
||||
* @type {Array<*>}
|
||||
* @private
|
||||
*/
|
||||
protected _blacklist: T[] = []
|
||||
|
||||
/**
|
||||
* Value to be compared in less than.
|
||||
* @type {*}
|
||||
* @private
|
||||
*/
|
||||
protected _lessThanValue?: T
|
||||
|
||||
/**
|
||||
* If true, the less than will be less than or equal to.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
protected _lessThanBit: boolean = false
|
||||
|
||||
/**
|
||||
* Value to be compared in greater than.
|
||||
* @type {*}
|
||||
* @private
|
||||
*/
|
||||
protected _greaterThanValue?: T
|
||||
|
||||
/**
|
||||
* If true, the greater than will be greater than or equal to.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
protected _greateerThanBit: boolean = false
|
||||
|
||||
/**
|
||||
* The value to be used to check equality.
|
||||
* @type {*}
|
||||
* @private
|
||||
*/
|
||||
protected _equalityValue?: T
|
||||
|
||||
/**
|
||||
* Whitelist the specified item or items and enable the whitelist.
|
||||
* @param {...*} items - the items to whitelist
|
||||
*/
|
||||
whitelist(...items: T[]) {
|
||||
this._useWhitelist = true
|
||||
items.forEach(item => this._whitelist.push(item))
|
||||
}
|
||||
|
||||
/**
|
||||
* Blacklist the specified item or items and enable the blacklist.
|
||||
* @param {...*} items - the items to blacklist
|
||||
*/
|
||||
blacklist(...items: T[]) {
|
||||
this._useBlacklist = true
|
||||
items.forEach(item => this._blacklist.push(item))
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the value to be used in less-than comparison and enables less-than comparison.
|
||||
* @param {*} value
|
||||
*/
|
||||
lessThan(value: T) {
|
||||
this._useLessThan = true
|
||||
this._lessThanValue = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the value to be used in less-than or equal-to comparison and enables that comparison.
|
||||
* @param {*} value
|
||||
*/
|
||||
lessThanOrEqualTo(value: T) {
|
||||
this._lessThanBit = true
|
||||
this.lessThan(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the value to be used in greater-than comparison and enables that comparison.
|
||||
* @param {*} value
|
||||
*/
|
||||
greaterThan(value: T) {
|
||||
this._useGreaterThan = true
|
||||
this._greaterThanValue = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the value to be used in greater-than or equal-to comparison and enables that comparison.
|
||||
* @param {*} value
|
||||
*/
|
||||
greaterThanOrEqualTo(value: T) {
|
||||
this._greateerThanBit = true
|
||||
this.greaterThan(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the value to be used in equality comparison and enables that comparison.
|
||||
* @param {*} value
|
||||
*/
|
||||
equals(value: T) {
|
||||
this._useEquality = true
|
||||
this._equalityValue = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified value passes the configured comparisons.
|
||||
* @param {*} value
|
||||
* @returns {boolean}
|
||||
*/
|
||||
validate(value: any) {
|
||||
let is_valid = true
|
||||
if ( this._useEquality ) {
|
||||
is_valid = is_valid && (this._equalityValue === value)
|
||||
}
|
||||
|
||||
if ( this._useLessThan && typeof this._lessThanValue !== 'undefined' ) {
|
||||
if ( this._lessThanBit ) {
|
||||
is_valid = is_valid && (value <= this._lessThanValue)
|
||||
} else {
|
||||
is_valid = is_valid && (value < this._lessThanValue)
|
||||
}
|
||||
}
|
||||
|
||||
if ( this._useGreaterThan && typeof this._greaterThanValue !== 'undefined' ) {
|
||||
if ( this._greateerThanBit ) {
|
||||
is_valid = is_valid && (value >= this._greaterThanValue)
|
||||
} else {
|
||||
is_valid = is_valid && (value > this._greaterThanValue)
|
||||
}
|
||||
}
|
||||
|
||||
if ( this._useWhitelist ) {
|
||||
is_valid = is_valid && this._whitelist.some(x => {
|
||||
return x === value
|
||||
})
|
||||
}
|
||||
|
||||