@appsemble/cli 0.20.9 → 0.20.12

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 (295) hide show
  1. package/__fixtures__/getBlockConfigFromTypeScript/comments/index.ts +40 -0
  2. package/__fixtures__/getBlockConfigFromTypeScript/comments/tsconfig.json +7 -0
  3. package/__fixtures__/getBlockConfigFromTypeScript/duplicateActions/index.ts +34 -0
  4. package/__fixtures__/getBlockConfigFromTypeScript/duplicateActions/tsconfig.json +7 -0
  5. package/__fixtures__/getBlockConfigFromTypeScript/duplicateEventEmitters/index.ts +34 -0
  6. package/__fixtures__/getBlockConfigFromTypeScript/duplicateEventEmitters/tsconfig.json +7 -0
  7. package/__fixtures__/getBlockConfigFromTypeScript/duplicateEventListeners/index.ts +34 -0
  8. package/__fixtures__/getBlockConfigFromTypeScript/duplicateEventListeners/tsconfig.json +7 -0
  9. package/__fixtures__/getBlockConfigFromTypeScript/duplicateParameters/index.ts +34 -0
  10. package/__fixtures__/getBlockConfigFromTypeScript/duplicateParameters/tsconfig.json +7 -0
  11. package/__fixtures__/getBlockConfigFromTypeScript/fontawesomeParameters/index.ts +11 -0
  12. package/__fixtures__/getBlockConfigFromTypeScript/fontawesomeParameters/tsconfig.json +7 -0
  13. package/__fixtures__/getBlockConfigFromTypeScript/tsError/index.ts +4 -0
  14. package/__fixtures__/getBlockConfigFromTypeScript/tsError/tsconfig.json +8 -0
  15. package/__fixtures__/getBlockConfigFromTypeScript/valid/index.ts +28 -0
  16. package/__fixtures__/getBlockConfigFromTypeScript/valid/tsconfig.json +7 -0
  17. package/__fixtures__/makePayload/no-icon/output/block.js +1 -0
  18. package/__fixtures__/makePayload/with-icon/icon.svg +2 -0
  19. package/__fixtures__/makePayload/with-icon/output/block.js +1 -0
  20. package/assets/appsemblerc.schema.json +150 -0
  21. package/changed/added/.gitkeep +0 -0
  22. package/changed/changed/.gitkeep +0 -0
  23. package/changed/deprecated/.gitkeep +0 -0
  24. package/changed/fixed/.gitkeep +0 -0
  25. package/changed/removed/.gitkeep +0 -0
  26. package/changed/security/.gitkeep +0 -0
  27. package/{dist/commands → commands}/app/create.d.ts +0 -0
  28. package/{dist/commands → commands}/app/create.js +0 -0
  29. package/commands/app/create.js.map +1 -0
  30. package/commands/app/create.ts +104 -0
  31. package/{dist/commands → commands}/app/extract-messages.d.ts +0 -0
  32. package/{dist/commands → commands}/app/extract-messages.js +0 -0
  33. package/commands/app/extract-messages.js.map +1 -0
  34. package/commands/app/extract-messages.ts +56 -0
  35. package/{dist/commands → commands}/app/index.d.ts +0 -0
  36. package/{dist/commands → commands}/app/index.js +0 -0
  37. package/commands/app/index.js.map +1 -0
  38. package/commands/app/index.ts +14 -0
  39. package/{dist/commands → commands}/app/update.d.ts +0 -0
  40. package/{dist/commands → commands}/app/update.js +0 -0
  41. package/commands/app/update.js.map +1 -0
  42. package/commands/app/update.ts +111 -0
  43. package/{dist/commands → commands}/asset/create.d.ts +0 -0
  44. package/{dist/commands → commands}/asset/create.js +0 -0
  45. package/commands/asset/create.js.map +1 -0
  46. package/commands/asset/create.ts +84 -0
  47. package/{dist/commands → commands}/asset/index.d.ts +0 -0
  48. package/{dist/commands → commands}/asset/index.js +0 -0
  49. package/commands/asset/index.js.map +1 -0
  50. package/commands/asset/index.ts +12 -0
  51. package/{dist/commands → commands}/block/build.d.ts +0 -0
  52. package/{dist/commands → commands}/block/build.js +0 -0
  53. package/commands/block/build.js.map +1 -0
  54. package/commands/block/build.ts +32 -0
  55. package/{dist/commands → commands}/block/extract-messages.d.ts +0 -0
  56. package/{dist/commands → commands}/block/extract-messages.js +0 -0
  57. package/commands/block/extract-messages.js.map +1 -0
  58. package/commands/block/extract-messages.ts +41 -0
  59. package/{dist/commands → commands}/block/index.d.ts +0 -0
  60. package/{dist/commands → commands}/block/index.js +0 -0
  61. package/commands/block/index.js.map +1 -0
  62. package/commands/block/index.ts +14 -0
  63. package/{dist/commands → commands}/block/publish.d.ts +0 -0
  64. package/{dist/commands → commands}/block/publish.js +0 -0
  65. package/commands/block/publish.js.map +1 -0
  66. package/commands/block/publish.ts +58 -0
  67. package/{dist/commands → commands}/cleanupResources.d.ts +0 -0
  68. package/{dist/commands → commands}/cleanupResources.js +0 -0
  69. package/commands/cleanupResources.js.map +1 -0
  70. package/commands/cleanupResources.ts +45 -0
  71. package/{dist/commands → commands}/config/get.d.ts +0 -0
  72. package/{dist/commands → commands}/config/get.js +0 -0
  73. package/commands/config/get.js.map +1 -0
  74. package/commands/config/get.ts +26 -0
  75. package/{dist/commands → commands}/config/index.d.ts +0 -0
  76. package/{dist/commands → commands}/config/index.js +0 -0
  77. package/commands/config/index.js.map +1 -0
  78. package/commands/config/index.ts +13 -0
  79. package/{dist/commands → commands}/config/set.d.ts +0 -0
  80. package/{dist/commands → commands}/config/set.js +0 -0
  81. package/commands/config/set.js.map +1 -0
  82. package/commands/config/set.ts +44 -0
  83. package/{dist/commands → commands}/login.d.ts +0 -0
  84. package/{dist/commands → commands}/login.js +0 -0
  85. package/commands/login.js.map +1 -0
  86. package/commands/login.ts +5 -0
  87. package/{dist/commands → commands}/logout.d.ts +0 -0
  88. package/{dist/commands → commands}/logout.js +0 -0
  89. package/commands/logout.js.map +1 -0
  90. package/commands/logout.ts +5 -0
  91. package/{dist/commands → commands}/migrate.d.ts +0 -0
  92. package/{dist/commands → commands}/migrate.js +5 -2
  93. package/commands/migrate.js.map +1 -0
  94. package/commands/migrate.ts +50 -0
  95. package/{dist/commands → commands}/organization/create.d.ts +0 -0
  96. package/{dist/commands → commands}/organization/create.js +0 -0
  97. package/commands/organization/create.js.map +1 -0
  98. package/commands/organization/create.ts +65 -0
  99. package/{dist/commands → commands}/organization/index.d.ts +0 -0
  100. package/{dist/commands → commands}/organization/index.js +0 -0
  101. package/commands/organization/index.js.map +1 -0
  102. package/commands/organization/index.ts +13 -0
  103. package/{dist/commands → commands}/organization/update.d.ts +0 -0
  104. package/{dist/commands → commands}/organization/update.js +0 -0
  105. package/commands/organization/update.js.map +1 -0
  106. package/commands/organization/update.ts +65 -0
  107. package/{dist/commands → commands}/resource/create.d.ts +0 -0
  108. package/{dist/commands → commands}/resource/create.js +0 -0
  109. package/commands/resource/create.js.map +1 -0
  110. package/commands/resource/create.ts +78 -0
  111. package/{dist/commands → commands}/resource/index.d.ts +0 -0
  112. package/{dist/commands → commands}/resource/index.js +0 -0
  113. package/commands/resource/index.js.map +1 -0
  114. package/commands/resource/index.ts +13 -0
  115. package/{dist/commands → commands}/resource/update.d.ts +0 -0
  116. package/{dist/commands → commands}/resource/update.js +0 -0
  117. package/commands/resource/update.js.map +1 -0
  118. package/commands/resource/update.ts +77 -0
  119. package/{dist/commands → commands}/runCronJobs.d.ts +0 -0
  120. package/{dist/commands → commands}/runCronJobs.js +0 -0
  121. package/commands/runCronJobs.js.map +1 -0
  122. package/commands/runCronJobs.ts +45 -0
  123. package/{dist/commands → commands}/start.d.ts +0 -0
  124. package/{dist/commands → commands}/start.js +4 -0
  125. package/commands/start.js.map +1 -0
  126. package/commands/start.ts +140 -0
  127. package/{dist/commands → commands}/team/create.d.ts +0 -0
  128. package/{dist/commands → commands}/team/create.js +0 -0
  129. package/commands/team/create.js.map +1 -0
  130. package/commands/team/create.ts +60 -0
  131. package/{dist/commands → commands}/team/delete.d.ts +0 -0
  132. package/{dist/commands → commands}/team/delete.js +0 -0
  133. package/commands/team/delete.js.map +1 -0
  134. package/commands/team/delete.ts +54 -0
  135. package/{dist/commands → commands}/team/index.d.ts +0 -0
  136. package/{dist/commands → commands}/team/index.js +0 -0
  137. package/commands/team/index.js.map +1 -0
  138. package/commands/team/index.ts +15 -0
  139. package/{dist/commands → commands}/team/member/delete.d.ts +0 -0
  140. package/{dist/commands → commands}/team/member/delete.js +0 -0
  141. package/commands/team/member/delete.js.map +1 -0
  142. package/commands/team/member/delete.ts +61 -0
  143. package/{dist/commands → commands}/team/member/index.d.ts +0 -0
  144. package/{dist/commands → commands}/team/member/index.js +0 -0
  145. package/commands/team/member/index.js.map +1 -0
  146. package/commands/team/member/index.ts +14 -0
  147. package/{dist/commands → commands}/team/member/invite.d.ts +0 -0
  148. package/{dist/commands → commands}/team/member/invite.js +0 -0
  149. package/commands/team/member/invite.js.map +1 -0
  150. package/commands/team/member/invite.ts +61 -0
  151. package/{dist/commands → commands}/team/member/update.d.ts +0 -0
  152. package/{dist/commands → commands}/team/member/update.js +0 -0
  153. package/commands/team/member/update.js.map +1 -0
  154. package/commands/team/member/update.ts +70 -0
  155. package/{dist/commands → commands}/team/update.d.ts +0 -0
  156. package/{dist/commands → commands}/team/update.js +0 -0
  157. package/commands/team/update.js.map +1 -0
  158. package/commands/team/update.ts +60 -0
  159. package/index.d.ts +2 -0
  160. package/{dist/index.js → index.js} +2 -6
  161. package/index.js.map +1 -0
  162. package/index.ts +74 -0
  163. package/jest.config.js +1 -0
  164. package/jest.setup.d.ts +1 -0
  165. package/jest.setup.js +6 -0
  166. package/jest.setup.js.map +1 -0
  167. package/jest.setup.ts +4 -0
  168. package/lib/__snapshots__/getBlockConfigFromTypeScript.test.ts.snap +3872 -0
  169. package/{dist/lib → lib}/app.d.ts +17 -17
  170. package/{dist/lib → lib}/app.js +29 -29
  171. package/lib/app.js.map +1 -0
  172. package/lib/app.ts +797 -0
  173. package/{dist/lib → lib}/asset.d.ts +0 -0
  174. package/{dist/lib → lib}/asset.js +0 -0
  175. package/lib/asset.js.map +1 -0
  176. package/lib/asset.ts +43 -0
  177. package/{dist/lib → lib}/authentication.d.ts +3 -3
  178. package/{dist/lib → lib}/authentication.js +47 -15
  179. package/lib/authentication.js.map +1 -0
  180. package/lib/authentication.ts +193 -0
  181. package/{dist/lib → lib}/block.d.ts +16 -16
  182. package/{dist/lib → lib}/block.js +35 -26
  183. package/lib/block.js.map +1 -0
  184. package/lib/block.test.d.ts +1 -0
  185. package/lib/block.test.js +106 -0
  186. package/lib/block.test.js.map +1 -0
  187. package/lib/block.test.ts +103 -0
  188. package/lib/block.ts +384 -0
  189. package/{dist/lib → lib}/coercers.d.ts +2 -2
  190. package/{dist/lib → lib}/coercers.js +2 -2
  191. package/lib/coercers.js.map +1 -0
  192. package/lib/coercers.test.d.ts +1 -0
  193. package/lib/coercers.test.js +28 -0
  194. package/lib/coercers.test.js.map +1 -0
  195. package/lib/coercers.test.ts +27 -0
  196. package/lib/coercers.ts +57 -0
  197. package/{dist/lib → lib}/getBlockConfigFromTypeScript.d.ts +1 -1
  198. package/{dist/lib → lib}/getBlockConfigFromTypeScript.js +38 -36
  199. package/lib/getBlockConfigFromTypeScript.js.map +1 -0
  200. package/lib/getBlockConfigFromTypeScript.test.d.ts +1 -0
  201. package/lib/getBlockConfigFromTypeScript.test.js +238 -0
  202. package/lib/getBlockConfigFromTypeScript.test.js.map +1 -0
  203. package/lib/getBlockConfigFromTypeScript.test.ts +281 -0
  204. package/lib/getBlockConfigFromTypeScript.ts +354 -0
  205. package/{dist/lib → lib}/initAxios.d.ts +1 -1
  206. package/{dist/lib → lib}/initAxios.js +3 -4
  207. package/lib/initAxios.js.map +1 -0
  208. package/lib/initAxios.ts +16 -0
  209. package/{dist/lib → lib}/loadWebpackConfig.d.ts +3 -3
  210. package/{dist/lib → lib}/loadWebpackConfig.js +3 -3
  211. package/lib/loadWebpackConfig.js.map +1 -0
  212. package/lib/loadWebpackConfig.ts +59 -0
  213. package/{dist/lib → lib}/organization.d.ts +1 -1
  214. package/{dist/lib → lib}/organization.js +0 -0
  215. package/lib/organization.js.map +1 -0
  216. package/lib/organization.ts +105 -0
  217. package/{dist/lib → lib}/output.d.ts +0 -0
  218. package/{dist/lib → lib}/output.js +0 -0
  219. package/lib/output.js.map +1 -0
  220. package/lib/output.ts +19 -0
  221. package/{dist/lib → lib}/processCss.d.ts +1 -1
  222. package/{dist/lib → lib}/processCss.js +3 -3
  223. package/lib/processCss.js.map +1 -0
  224. package/lib/processCss.ts +24 -0
  225. package/{dist/lib → lib}/resource.d.ts +0 -0
  226. package/{dist/lib → lib}/resource.js +1 -1
  227. package/lib/resource.js.map +1 -0
  228. package/lib/resource.ts +128 -0
  229. package/{dist/lib → lib}/serverImport.d.ts +1 -1
  230. package/{dist/lib → lib}/serverImport.js +2 -2
  231. package/lib/serverImport.js.map +1 -0
  232. package/lib/serverImport.ts +43 -0
  233. package/{dist/lib → lib}/team.d.ts +0 -0
  234. package/{dist/lib → lib}/team.js +0 -0
  235. package/lib/team.js.map +1 -0
  236. package/lib/team.ts +152 -0
  237. package/package.json +14 -12
  238. package/tsconfig.json +10 -0
  239. package/{dist/types.d.ts → types.d.ts} +0 -0
  240. package/{dist/types.js → types.js} +0 -0
  241. package/types.js.map +1 -0
  242. package/types.ts +110 -0
  243. package/dist/commands/app/create.js.map +0 -1
  244. package/dist/commands/app/extract-messages.js.map +0 -1
  245. package/dist/commands/app/index.js.map +0 -1
  246. package/dist/commands/app/update.js.map +0 -1
  247. package/dist/commands/asset/create.js.map +0 -1
  248. package/dist/commands/asset/index.js.map +0 -1
  249. package/dist/commands/block/build.js.map +0 -1
  250. package/dist/commands/block/extract-messages.js.map +0 -1
  251. package/dist/commands/block/index.js.map +0 -1
  252. package/dist/commands/block/publish.js.map +0 -1
  253. package/dist/commands/cleanupResources.js.map +0 -1
  254. package/dist/commands/config/get.js.map +0 -1
  255. package/dist/commands/config/index.js.map +0 -1
  256. package/dist/commands/config/set.js.map +0 -1
  257. package/dist/commands/login.js.map +0 -1
  258. package/dist/commands/logout.js.map +0 -1
  259. package/dist/commands/migrate.js.map +0 -1
  260. package/dist/commands/organization/create.js.map +0 -1
  261. package/dist/commands/organization/index.js.map +0 -1
  262. package/dist/commands/organization/update.js.map +0 -1
  263. package/dist/commands/resource/create.js.map +0 -1
  264. package/dist/commands/resource/index.js.map +0 -1
  265. package/dist/commands/resource/update.js.map +0 -1
  266. package/dist/commands/runCronJobs.js.map +0 -1
  267. package/dist/commands/start.js.map +0 -1
  268. package/dist/commands/team/create.js.map +0 -1
  269. package/dist/commands/team/delete.js.map +0 -1
  270. package/dist/commands/team/index.js.map +0 -1
  271. package/dist/commands/team/member/delete.js.map +0 -1
  272. package/dist/commands/team/member/index.js.map +0 -1
  273. package/dist/commands/team/member/invite.js.map +0 -1
  274. package/dist/commands/team/member/update.js.map +0 -1
  275. package/dist/commands/team/update.js.map +0 -1
  276. package/dist/index.d.ts +0 -2
  277. package/dist/index.js.map +0 -1
  278. package/dist/lib/app.js.map +0 -1
  279. package/dist/lib/asset.js.map +0 -1
  280. package/dist/lib/authentication.js.map +0 -1
  281. package/dist/lib/block.js.map +0 -1
  282. package/dist/lib/coercers.js.map +0 -1
  283. package/dist/lib/getBlockConfigFromTypeScript.js.map +0 -1
  284. package/dist/lib/initAxios.js.map +0 -1
  285. package/dist/lib/loadWebpackConfig.js.map +0 -1
  286. package/dist/lib/organization.js.map +0 -1
  287. package/dist/lib/output.js.map +0 -1
  288. package/dist/lib/processCss.js.map +0 -1
  289. package/dist/lib/readPackageJson.d.ts +0 -9
  290. package/dist/lib/readPackageJson.js +0 -16
  291. package/dist/lib/readPackageJson.js.map +0 -1
  292. package/dist/lib/resource.js.map +0 -1
  293. package/dist/lib/serverImport.js.map +0 -1
  294. package/dist/lib/team.js.map +0 -1
  295. package/dist/types.js.map +0 -1
@@ -0,0 +1,281 @@
1
+ import { readdirSync } from 'fs';
2
+ import { join, resolve } from 'path';
3
+
4
+ import { AppsembleError, resolveFixture } from '@appsemble/node-utils';
5
+ import ts from 'typescript';
6
+
7
+ import { getBlockConfigFromTypeScript } from './getBlockConfigFromTypeScript';
8
+
9
+ beforeEach(() => {
10
+ jest.spyOn(process, 'cwd').mockReturnValue(resolveFixture('.'));
11
+ });
12
+
13
+ describe('getBlockConfigFromTypeScript', () => {
14
+ it('should extract configuration from a TypeScript project', () => {
15
+ const result = getBlockConfigFromTypeScript({
16
+ name: '',
17
+ layout: 'float',
18
+ version: '1.33.7',
19
+ webpack: '',
20
+ output: '',
21
+ dir: resolveFixture('getBlockConfigFromTypeScript/valid'),
22
+ });
23
+ expect(result).toStrictEqual({
24
+ actions: { testAction: { description: undefined } },
25
+ events: {
26
+ emit: { testEmit: { description: undefined } },
27
+ listen: { testListener: { description: undefined } },
28
+ },
29
+ messages: undefined,
30
+ parameters: {
31
+ $schema: 'http://json-schema.org/draft-07/schema#',
32
+ additionalProperties: false,
33
+ properties: {
34
+ param1: { type: 'string' },
35
+ param2: { type: 'number' },
36
+ param3: { type: 'boolean' },
37
+ param4: { type: 'string' },
38
+ param5: {
39
+ additionalProperties: false,
40
+ properties: {
41
+ nested1: { type: 'string' },
42
+ nested2: { type: 'number' },
43
+ nested3: { type: 'boolean' },
44
+ },
45
+ required: ['nested1', 'nested2', 'nested3'],
46
+ type: 'object',
47
+ },
48
+ param6: { items: { type: 'string' }, type: 'array' },
49
+ },
50
+ required: ['param1', 'param2', 'param3', 'param5', 'param6'],
51
+ type: 'object',
52
+ },
53
+ });
54
+ });
55
+
56
+ it('should not use TypeScript if all metadata is present in the original config', () => {
57
+ jest.spyOn(ts, 'createProgram');
58
+ const input = {
59
+ actions: {},
60
+ events: { emit: { foo: {} }, listen: { bar: {} } },
61
+ layout: 'float',
62
+ parameters: { type: 'object' },
63
+ version: '1.33.7',
64
+ webpack: '',
65
+ output: '',
66
+ dir: resolveFixture('getBlockConfigFromTypeScript/valid'),
67
+ name: '',
68
+ } as const;
69
+ const result = getBlockConfigFromTypeScript(input);
70
+
71
+ expect(result).toBe(input);
72
+ expect(ts.createProgram).not.toHaveBeenCalled();
73
+ });
74
+
75
+ it('should prefer actions overrides over TypeScript actions', () => {
76
+ const result = getBlockConfigFromTypeScript({
77
+ actions: {},
78
+ version: '1.33.7',
79
+ webpack: '',
80
+ output: '',
81
+ dir: resolveFixture('getBlockConfigFromTypeScript/valid'),
82
+ name: '',
83
+ });
84
+
85
+ expect(result.actions).toStrictEqual({});
86
+ });
87
+
88
+ it('should prefer events overrides over TypeScript events', () => {
89
+ const result = getBlockConfigFromTypeScript({
90
+ events: { emit: { onSuccess: {} } },
91
+ version: '1.33.7',
92
+ webpack: '',
93
+ output: '',
94
+ dir: resolveFixture('getBlockConfigFromTypeScript/valid'),
95
+ name: '',
96
+ });
97
+
98
+ expect(result.events).toStrictEqual({ emit: { onSuccess: {} } });
99
+ });
100
+
101
+ it('should prefer parameters overrides over TypeScript parameters', () => {
102
+ const result = getBlockConfigFromTypeScript({
103
+ parameters: { type: 'object' },
104
+ version: '1.33.7',
105
+ webpack: '',
106
+ output: '',
107
+ dir: resolveFixture('getBlockConfigFromTypeScript/valid'),
108
+ name: '',
109
+ });
110
+
111
+ expect(result.parameters).toStrictEqual({ type: 'object' });
112
+ });
113
+
114
+ it('should throw if duplicate Actions are found', () => {
115
+ expect(() =>
116
+ getBlockConfigFromTypeScript({
117
+ webpack: '',
118
+ output: '',
119
+ dir: resolveFixture('getBlockConfigFromTypeScript/duplicateActions'),
120
+ name: '',
121
+ version: '1.33.7',
122
+ }),
123
+ ).toThrow(
124
+ new AppsembleError(
125
+ "Found duplicate interface 'Actions' in 'getBlockConfigFromTypeScript/duplicateActions/index.ts:31'",
126
+ ),
127
+ );
128
+ });
129
+
130
+ it('should throw if duplicate EventEmitters are found', () => {
131
+ expect(() =>
132
+ getBlockConfigFromTypeScript({
133
+ webpack: '',
134
+ output: '',
135
+ dir: resolveFixture('getBlockConfigFromTypeScript/duplicateEventEmitters'),
136
+ name: '',
137
+ version: '1.33.7',
138
+ }),
139
+ ).toThrow(
140
+ new AppsembleError(
141
+ "Found duplicate interface 'EventEmitters' in 'getBlockConfigFromTypeScript/duplicateEventEmitters/index.ts:31'",
142
+ ),
143
+ );
144
+ });
145
+
146
+ it('should throw if duplicate EventListeners are found', () => {
147
+ expect(() =>
148
+ getBlockConfigFromTypeScript({
149
+ webpack: '',
150
+ output: '',
151
+ dir: resolveFixture('getBlockConfigFromTypeScript/duplicateEventListeners'),
152
+ name: '',
153
+ version: '1.33.7',
154
+ }),
155
+ ).toThrow(
156
+ new AppsembleError(
157
+ "Found duplicate interface 'EventListeners' in 'getBlockConfigFromTypeScript/duplicateEventListeners/index.ts:31'",
158
+ ),
159
+ );
160
+ });
161
+
162
+ it('should throw if duplicate Parameters are found', () => {
163
+ expect(() =>
164
+ getBlockConfigFromTypeScript({
165
+ webpack: '',
166
+ output: '',
167
+ dir: resolveFixture('getBlockConfigFromTypeScript/duplicateParameters'),
168
+ name: '',
169
+ version: '1.33.7',
170
+ }),
171
+ ).toThrow(
172
+ new AppsembleError(
173
+ "Found duplicate interface 'Parameters' in 'getBlockConfigFromTypeScript/duplicateParameters/index.ts:31'",
174
+ ),
175
+ );
176
+ });
177
+
178
+ it('should handle fontawesome icons', () => {
179
+ const result = getBlockConfigFromTypeScript({
180
+ webpack: '',
181
+ output: '',
182
+ dir: resolveFixture('getBlockConfigFromTypeScript/fontawesomeParameters'),
183
+ name: '',
184
+ version: '1.33.7',
185
+ });
186
+
187
+ expect(result).toStrictEqual({
188
+ actions: undefined,
189
+ events: undefined,
190
+ messages: undefined,
191
+ parameters: {
192
+ $schema: 'http://json-schema.org/draft-07/schema#',
193
+ additionalProperties: false,
194
+ properties: {
195
+ icon: { description: 'This is an icon.', format: 'fontawesome', type: 'string' },
196
+ },
197
+ type: 'object',
198
+ },
199
+ });
200
+ });
201
+
202
+ it('should handle TypeScript pre emit diagnostics', () => {
203
+ function fn(): void {
204
+ getBlockConfigFromTypeScript({
205
+ name: '',
206
+ layout: 'float',
207
+ version: '1.33.7',
208
+ webpack: '',
209
+ output: '',
210
+ dir: resolveFixture('getBlockConfigFromTypeScript/tsError'),
211
+ });
212
+ }
213
+ expect(fn).toThrow(AppsembleError);
214
+ expect(fn).toThrow(/'unused' is declared but its value is never read/);
215
+ });
216
+
217
+ it('should extract comments', () => {
218
+ const result = getBlockConfigFromTypeScript({
219
+ name: '',
220
+ layout: 'float',
221
+ version: '1.33.7',
222
+ webpack: '',
223
+ output: '',
224
+ dir: resolveFixture('getBlockConfigFromTypeScript/comments'),
225
+ });
226
+
227
+ expect(result).toStrictEqual({
228
+ actions: {
229
+ comment: {
230
+ description: 'Valid action comment',
231
+ },
232
+ duplicate: {
233
+ description: 'Expected comment',
234
+ },
235
+ line: {
236
+ description: undefined,
237
+ },
238
+ },
239
+ events: {
240
+ emit: {
241
+ testEmit: {
242
+ description: 'Test event emitter.',
243
+ },
244
+ },
245
+ listen: {
246
+ testListener: {
247
+ description: 'Test event listener.',
248
+ },
249
+ },
250
+ },
251
+ messages: undefined,
252
+ parameters: {
253
+ $schema: 'http://json-schema.org/draft-07/schema#',
254
+ additionalProperties: false,
255
+ properties: {
256
+ param: {},
257
+ },
258
+ required: ['param'],
259
+ type: 'object',
260
+ },
261
+ });
262
+ });
263
+
264
+ describe('official blocks', () => {
265
+ const blocksDir = resolve(__dirname, '..', '..', '..', 'blocks');
266
+
267
+ it.each(readdirSync(blocksDir))('%s', (name) => {
268
+ const dir = join(blocksDir, name);
269
+
270
+ const result = getBlockConfigFromTypeScript({
271
+ name,
272
+ version: '',
273
+ output: '',
274
+ webpack: undefined,
275
+ dir,
276
+ });
277
+
278
+ expect(result).toMatchSnapshot();
279
+ });
280
+ });
281
+ });
@@ -0,0 +1,354 @@
1
+ import { relative } from 'path';
2
+
3
+ import { AppsembleError, logger } from '@appsemble/node-utils';
4
+ import { BlockConfig, BlockManifest } from '@appsemble/types';
5
+ import { parse } from 'comment-parser';
6
+ import { Schema } from 'jsonschema';
7
+ import normalizePath from 'normalize-path';
8
+ import { createFormatter, createParser, SchemaGenerator } from 'ts-json-schema-generator';
9
+ import ts from 'typescript';
10
+
11
+ /**
12
+ * Get the tsdoc comment for a TypeScript node.
13
+ *
14
+ * @param checker The type checker instance to use.
15
+ * @param node The node for which to get the tsdoc.
16
+ * @returns The tsdoc comment as a string, if present.
17
+ */
18
+ function getNodeComments(checker: ts.TypeChecker, node: ts.TypeElement): string {
19
+ const symbol = checker.getSymbolAtLocation(node.name);
20
+ if (!symbol) {
21
+ return;
22
+ }
23
+
24
+ const comments = symbol.getDocumentationComment(checker);
25
+ if (comments.length) {
26
+ return comments
27
+ .map((comment) =>
28
+ comment.kind === 'lineBreak' ? comment.text : comment.text.trim().replace(/\r\n/g, '\n'),
29
+ )
30
+ .join('');
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Process keys from an interface into an object.
36
+ *
37
+ * This asserts only index signature members and regular alphanumerical properties are used.
38
+ *
39
+ * @param iface The TypeScript interface to check.
40
+ * @param checker The TypeScript type checker.
41
+ * @param convert A function for converting extracted data to a value. It will be called with the
42
+ * name and JSDoc description. The name is `undefined` for index signatures. The function should
43
+ * return a tuple of key and value.
44
+ * @returns A record created from the returned key/value pairs.
45
+ */
46
+ function processInterface<T>(
47
+ iface: ts.InterfaceDeclaration,
48
+ checker: ts.TypeChecker,
49
+ convert: (name: string, description: string) => [string, T],
50
+ ): Record<string, T> {
51
+ if (!iface?.members.length) {
52
+ return;
53
+ }
54
+
55
+ const source = iface.getSourceFile().getText();
56
+
57
+ return Object.fromEntries(
58
+ iface.members.map((member) => {
59
+ const description = getNodeComments(checker, member);
60
+ if (ts.isIndexSignatureDeclaration(member)) {
61
+ // Comments aren’t properly extracted for index signatures.
62
+ const commentRanges = ts.getLeadingCommentRanges(source, member.pos);
63
+ if (commentRanges) {
64
+ const comments = commentRanges?.map((r) => source.slice(r.pos, r.end)) ?? [];
65
+ const [block] = parse(comments[0]);
66
+ const processedDescription = block.source
67
+ .map((src) => src.tokens.description)
68
+ .join('\n')
69
+ .replace(/^\s+|\s+$/g, '');
70
+
71
+ return convert(undefined, processedDescription);
72
+ }
73
+
74
+ return convert(undefined, description);
75
+ }
76
+
77
+ if (!ts.isPropertySignature(member)) {
78
+ throw new AppsembleError(
79
+ `Only property and index signatures are allowed as ${
80
+ iface.name
81
+ } keys. Found: ${member.getFullText()}`,
82
+ );
83
+ }
84
+
85
+ const { name } = member;
86
+
87
+ if (!ts.isIdentifier(name)) {
88
+ throw new AppsembleError(
89
+ `Only property and index signatures are allowed as ${
90
+ iface.name
91
+ } keys. Found: ${name.getFullText()}`,
92
+ );
93
+ }
94
+
95
+ const { text } = name;
96
+
97
+ if (!/^[\da-z]+$/i.test(text)) {
98
+ throw new AppsembleError(
99
+ `Found property named ‘${text}’ in ${iface.name} interface. Only alphanumerical identifiers are supported.`,
100
+ );
101
+ }
102
+
103
+ return convert(text, description);
104
+ }),
105
+ );
106
+ }
107
+
108
+ /**
109
+ * Get an actions object based on a TypeScript interface node.
110
+ *
111
+ * @param iface The node to base the actions on.
112
+ * @param checker The TypeScript type checker.
113
+ * @returns The action manifest to upload.
114
+ */
115
+ function processActions(
116
+ iface: ts.InterfaceDeclaration,
117
+ checker: ts.TypeChecker,
118
+ ): BlockManifest['actions'] {
119
+ return processInterface(iface, checker, (name, description) => [name ?? '$any', { description }]);
120
+ }
121
+
122
+ /**
123
+ * Get an events object based on TypeScript interface nodes.
124
+ *
125
+ * @param eventListenerInterface The node to base the event listeners on.
126
+ * @param eventEmitterInterface The node to base the event emitters on.
127
+ * @param checker The TypeScript type checker.
128
+ * @returns The events manifest to upload.
129
+ */
130
+ function processEvents(
131
+ eventListenerInterface: ts.InterfaceDeclaration,
132
+ eventEmitterInterface: ts.InterfaceDeclaration,
133
+ checker: ts.TypeChecker,
134
+ ): BlockManifest['events'] {
135
+ const listen = processInterface(eventListenerInterface, checker, (name, description) => [
136
+ name ?? '$any',
137
+ { description },
138
+ ]);
139
+ const emit = processInterface(eventEmitterInterface, checker, (name, description) => [
140
+ name ?? '$any',
141
+ { description },
142
+ ]);
143
+
144
+ return (emit || listen) && { emit, listen };
145
+ }
146
+
147
+ /**
148
+ * Get the JSON schema for parameters based on a TypeScript program.
149
+ *
150
+ * @param program The TypeScript program from which to extract parameters.
151
+ * @param iface The interface node from which to extract parameters.
152
+ * @returns The JSON schema for the block parameters.
153
+ */
154
+ function processParameters(program: ts.Program, iface: ts.InterfaceDeclaration): Schema {
155
+ if (!iface) {
156
+ return;
157
+ }
158
+ const formatter = createFormatter({}, (fmt) => {
159
+ fmt.addTypeFormatter({
160
+ supportsType: (type) => type.getName() === 'IconName',
161
+ getDefinition: () => ({ type: 'string', format: 'fontawesome' }),
162
+ getChildren: () => [],
163
+ });
164
+ fmt.addTypeFormatter({
165
+ supportsType: (type) => type.getName() === 'Remapper',
166
+ getDefinition: () => ({ format: 'remapper' }),
167
+ getChildren: () => [],
168
+ });
169
+ });
170
+ const parser = createParser(program, { expose: 'all', topRef: false });
171
+ const generator = new SchemaGenerator(program, parser, formatter);
172
+ const schema = generator.createSchemaFromNodes([iface]) as Schema;
173
+ if (schema.definitions && !Object.keys(schema.definitions).length) {
174
+ delete schema.definitions;
175
+ }
176
+ // This is the tsdoc that has been added to the SDK to aid the block developer.
177
+ delete schema.description;
178
+ return schema;
179
+ }
180
+
181
+ /**
182
+ * Get a messages object based on a TypeScript interface node.
183
+ *
184
+ * @param iface The node to base the messages on.
185
+ * @param checker The TypeScript type checker.
186
+ * @returns The action manifest to upload.
187
+ */
188
+ function processMessages(
189
+ iface: ts.InterfaceDeclaration,
190
+ checker: ts.TypeChecker,
191
+ ): BlockManifest['messages'] {
192
+ return processInterface(iface, checker, (name, description) => [name, { description }]);
193
+ }
194
+
195
+ /**
196
+ * Get the TypeScript program for a given path.
197
+ *
198
+ * @param blockPath The path for which to get the TypeScript program.
199
+ * @returns The TypeScript program.
200
+ */
201
+ function getProgram(blockPath: string): ts.Program {
202
+ const diagnosticHost: ts.FormatDiagnosticsHost = {
203
+ getNewLine: () => ts.sys.newLine,
204
+ getCurrentDirectory: ts.sys.getCurrentDirectory,
205
+ getCanonicalFileName: (x) => x,
206
+ };
207
+ const tsConfigPath = ts.findConfigFile(blockPath, ts.sys.fileExists);
208
+ const { config, error } = ts.readConfigFile(tsConfigPath, ts.sys.readFile);
209
+ if (error) {
210
+ throw new AppsembleError(ts.formatDiagnostic(error, diagnosticHost));
211
+ }
212
+ if (!config.files || !config.include) {
213
+ config.files = ts.sys
214
+ .readDirectory(blockPath, ['.ts', '.tsx'])
215
+ .map((f) => relative(blockPath, f));
216
+ }
217
+ const { errors, fileNames, options } = ts.parseJsonConfigFileContent(
218
+ config,
219
+ ts.sys,
220
+ blockPath,
221
+ undefined,
222
+ tsConfigPath,
223
+ );
224
+ // Filter: 'rootDir' is expected to contain all source files.
225
+ const diagnostics = errors.filter(({ code }) => code !== 6059);
226
+ if (diagnostics.length) {
227
+ throw new AppsembleError(ts.formatDiagnosticsWithColorAndContext(diagnostics, diagnosticHost));
228
+ }
229
+
230
+ options.noEmit = true;
231
+ delete options.out;
232
+ delete options.outDir;
233
+ delete options.outFile;
234
+ delete options.declaration;
235
+ delete options.declarationDir;
236
+ delete options.declarationMap;
237
+ const program = ts.createProgram(fileNames, options);
238
+ const preEmitDiagnostics = ts.getPreEmitDiagnostics(program);
239
+ if (preEmitDiagnostics.length) {
240
+ throw new AppsembleError(
241
+ ts.formatDiagnosticsWithColorAndContext(preEmitDiagnostics, diagnosticHost),
242
+ );
243
+ }
244
+ return program;
245
+ }
246
+
247
+ /**
248
+ * Generate a block manifest from the block metadata and TypeScript project.
249
+ *
250
+ * Uses the .appsemblerc file and the type definitions of the block.
251
+ *
252
+ * @param blockConfig The block configuration
253
+ * @returns The block configuration appended from the TypeScript project.
254
+ */
255
+ export function getBlockConfigFromTypeScript(
256
+ blockConfig: BlockConfig,
257
+ ): Pick<BlockManifest, 'actions' | 'events' | 'messages' | 'parameters'> {
258
+ if ('actions' in blockConfig && 'events' in blockConfig && 'parameters' in blockConfig) {
259
+ return blockConfig;
260
+ }
261
+ logger.info(`Extracting data from TypeScript project ${blockConfig.dir}`);
262
+ const program = getProgram(blockConfig.dir);
263
+ const checker = program.getTypeChecker();
264
+
265
+ let actionInterface: ts.InterfaceDeclaration;
266
+ let eventEmitterInterface: ts.InterfaceDeclaration;
267
+ let eventListenerInterface: ts.InterfaceDeclaration;
268
+ let messagesInterface: ts.InterfaceDeclaration;
269
+ let patametersInterface: ts.InterfaceDeclaration;
270
+
271
+ for (const sourceFile of program.getSourceFiles()) {
272
+ const fileName = relative(process.cwd(), sourceFile.fileName);
273
+ // Filter TypeScript default libs
274
+ if (program.isSourceFileDefaultLibrary(sourceFile)) {
275
+ logger.silly(`Skipping metadata extraction from: ${fileName}`);
276
+ continue;
277
+ }
278
+ logger.verbose(`Searching metadata in: ${fileName}`);
279
+ // eslint-disable-next-line @typescript-eslint/no-loop-func
280
+ ts.forEachChild(sourceFile, (mod) => {
281
+ // This node doesn’t override SDK types
282
+ if (!ts.isModuleDeclaration(mod)) {
283
+ return;
284
+ }
285
+ // This module defines other types
286
+ if (mod.name.text !== '@appsemble/sdk') {
287
+ return;
288
+ }
289
+ ts.forEachChild(mod.body, (iface) => {
290
+ // Appsemble only uses module interface augmentation.
291
+ if (!ts.isInterfaceDeclaration(iface)) {
292
+ return;
293
+ }
294
+ const { line } = sourceFile.getLineAndCharacterOfPosition(iface.getStart(sourceFile));
295
+ // Line numbers are 0 indexed, whereas they are usually represented as 1 indexed.
296
+ const loc = `${normalizePath(fileName)}:${line + 1}`;
297
+
298
+ switch (iface.name.text) {
299
+ case 'Actions':
300
+ logger.info(`Found augmented interface 'Actions' in '${loc}'`);
301
+ if (actionInterface) {
302
+ throw new AppsembleError(`Found duplicate interface 'Actions' in '${loc}'`);
303
+ }
304
+ actionInterface = iface;
305
+ break;
306
+ case 'EventEmitters':
307
+ logger.info(`Found augmented interface 'EventEmitters' in '${loc}'`);
308
+ if (eventEmitterInterface) {
309
+ throw new AppsembleError(`Found duplicate interface 'EventEmitters' in '${loc}'`);
310
+ }
311
+ eventEmitterInterface = iface;
312
+ break;
313
+ case 'EventListeners':
314
+ logger.info(`Found augmented interface 'EventListeners' in '${loc}'`);
315
+ if (eventListenerInterface) {
316
+ throw new AppsembleError(`Found duplicate interface 'EventListeners' in '${loc}'`);
317
+ }
318
+ eventListenerInterface = iface;
319
+ break;
320
+ case 'Parameters':
321
+ logger.info(`Found augmented interface 'Parameters' in '${loc}'`);
322
+ if (patametersInterface) {
323
+ throw new AppsembleError(`Found duplicate interface 'Parameters' in '${loc}'`);
324
+ }
325
+ patametersInterface = iface;
326
+ break;
327
+ case 'Messages':
328
+ logger.info(`Found augmented interface 'Messages' in '${loc}'`);
329
+ messagesInterface = iface;
330
+ break;
331
+ default:
332
+ logger.warn(`Detected unused augmented type ${iface.name.text} in ${loc}`);
333
+ }
334
+ });
335
+ });
336
+ }
337
+
338
+ return {
339
+ actions:
340
+ 'actions' in blockConfig ? blockConfig.actions : processActions(actionInterface, checker),
341
+ events:
342
+ 'events' in blockConfig
343
+ ? blockConfig.events
344
+ : processEvents(eventListenerInterface, eventEmitterInterface, checker),
345
+ parameters:
346
+ 'parameters' in blockConfig
347
+ ? blockConfig.parameters
348
+ : processParameters(program, patametersInterface),
349
+ messages:
350
+ 'messages' in blockConfig
351
+ ? blockConfig.messages
352
+ : processMessages(messagesInterface, checker),
353
+ };
354
+ }
@@ -2,6 +2,6 @@ import { BaseArguments } from '../types';
2
2
  /**
3
3
  * Configure the default axios URL.
4
4
  *
5
- * @param argv - The parsed command line arguments.
5
+ * @param argv The parsed command line arguments.
6
6
  */
7
7
  export declare function initAxios({ remote }: BaseArguments): void;
@@ -6,17 +6,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.initAxios = void 0;
7
7
  const node_utils_1 = require("@appsemble/node-utils");
8
8
  const axios_1 = __importDefault(require("axios"));
9
- const readPackageJson_1 = require("./readPackageJson");
9
+ const package_json_1 = __importDefault(require("../package.json"));
10
10
  /**
11
11
  * Configure the default axios URL.
12
12
  *
13
- * @param argv - The parsed command line arguments.
13
+ * @param argv The parsed command line arguments.
14
14
  */
15
15
  function initAxios({ remote }) {
16
- const { version } = (0, readPackageJson_1.readPackageJson)();
17
16
  axios_1.default.defaults.baseURL = remote;
18
17
  node_utils_1.logger.verbose(`Request remote set to ${remote}`);
19
- (0, node_utils_1.configureAxios)('AppsembleCLI', version);
18
+ (0, node_utils_1.configureAxios)('AppsembleCLI', package_json_1.default.version);
20
19
  }
21
20
  exports.initAxios = initAxios;
22
21
  //# sourceMappingURL=initAxios.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initAxios.js","sourceRoot":"","sources":["initAxios.ts"],"names":[],"mappings":";;;;;;AAAA,sDAA+D;AAC/D,kDAA0B;AAE1B,mEAAkC;AAGlC;;;;GAIG;AACH,SAAgB,SAAS,CAAC,EAAE,MAAM,EAAiB;IACjD,eAAK,CAAC,QAAQ,CAAC,OAAO,GAAG,MAAM,CAAC;IAChC,mBAAM,CAAC,OAAO,CAAC,yBAAyB,MAAM,EAAE,CAAC,CAAC;IAClD,IAAA,2BAAc,EAAC,cAAc,EAAE,sBAAG,CAAC,OAAO,CAAC,CAAC;AAC9C,CAAC;AAJD,8BAIC"}
@@ -0,0 +1,16 @@
1
+ import { configureAxios, logger } from '@appsemble/node-utils';
2
+ import axios from 'axios';
3
+
4
+ import pkg from '../package.json';
5
+ import { BaseArguments } from '../types';
6
+
7
+ /**
8
+ * Configure the default axios URL.
9
+ *
10
+ * @param argv The parsed command line arguments.
11
+ */
12
+ export function initAxios({ remote }: BaseArguments): void {
13
+ axios.defaults.baseURL = remote;
14
+ logger.verbose(`Request remote set to ${remote}`);
15
+ configureAxios('AppsembleCLI', pkg.version);
16
+ }
@@ -7,9 +7,9 @@ import { Configuration } from 'webpack';
7
7
  * or asynchronous function which returns a webpack configuration object. This function supports
8
8
  * all 3 use cases.
9
9
  *
10
- * @param block - The path to the webpack configuration file.
11
- * @param mode - The env that would be passed to webpack by invoking `webpack --env $env`.
12
- * @param outputPath - The path where the build will be output on disk.
10
+ * @param block The path to the webpack configuration file.
11
+ * @param mode The env that would be passed to webpack by invoking `webpack --env $env`.
12
+ * @param outputPath The path where the build will be output on disk.
13
13
  * @returns The webpack configuration as exposed by the webpack configuration file.
14
14
  */
15
15
  export declare function loadWebpackConfig(block: BlockConfig, mode?: 'development' | 'production', outputPath?: string): Promise<Configuration>;