@projectinvicta/nails 2.0.15

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 (164) hide show
  1. package/README.md +419 -0
  2. package/bin/lib/init.js +103 -0
  3. package/bin/lib/nails.c +81 -0
  4. package/bin/lib/nails.js +23 -0
  5. package/bin/lib/test.js +4 -0
  6. package/index.js +6 -0
  7. package/lib/application.js +24 -0
  8. package/lib/collection.js +6 -0
  9. package/lib/controller.js +182 -0
  10. package/lib/database_connector.js +12 -0
  11. package/lib/firebase_connector.js +94 -0
  12. package/lib/model_v2.js +24 -0
  13. package/lib/mongoose_connector.js +49 -0
  14. package/lib/mongoose_mem_connector.js +21 -0
  15. package/lib/nails.js +244 -0
  16. package/lib/router.js +202 -0
  17. package/lib/sequelize_connector.js +31 -0
  18. package/lib/server.js +1 -0
  19. package/package.json +60 -0
  20. package/scripts/install.js +57 -0
  21. package/scripts/uninstall.js +23 -0
  22. package/spec/bin/init.spec.js +0 -0
  23. package/spec/controller.spec.js +105 -0
  24. package/spec/model_v2.spec.js +75 -0
  25. package/spec/mongodb_connector.util.js +30 -0
  26. package/spec/mongoose_connector.util.js +20 -0
  27. package/spec/nails.spec.js +0 -0
  28. package/spec/router.spec.js +101 -0
  29. package/spec/sequelize_connector.spec.js +91 -0
  30. package/spec/sequelize_connector.util.js +18 -0
  31. package/spec/services/integration/README.md +5 -0
  32. package/spec/services/integration/client/css/styles.css +0 -0
  33. package/spec/services/integration/client/download.jpg +0 -0
  34. package/spec/services/integration/client/favicon.ico +0 -0
  35. package/spec/services/integration/client/index.html +9 -0
  36. package/spec/services/integration/client/js/client.js +0 -0
  37. package/spec/services/integration/client/js/components/app.jsx +15 -0
  38. package/spec/services/integration/config/db.js +14 -0
  39. package/spec/services/integration/config/mimes.js +61 -0
  40. package/spec/services/integration/config/routes.js +41 -0
  41. package/spec/services/integration/config/service.js +48 -0
  42. package/spec/services/integration/config/ssl/certificate.pem +22 -0
  43. package/spec/services/integration/config/ssl/csr.csr +17 -0
  44. package/spec/services/integration/config/ssl/key.pem +28 -0
  45. package/spec/services/integration/config/ssl/private_key.pem +30 -0
  46. package/spec/services/integration/config/ssl/public_key.pem +9 -0
  47. package/spec/services/integration/package.json +23 -0
  48. package/spec/services/integration/server/controllers/classbased_controller.js +33 -0
  49. package/spec/services/integration/server/controllers/default_json_controller.js +20 -0
  50. package/spec/services/integration/server/controllers/error_controller.js +27 -0
  51. package/spec/services/integration/server/controllers/home_controller.js +39 -0
  52. package/spec/services/integration/server/controllers/json_controller.js +15 -0
  53. package/spec/services/integration/server/controllers/manualrenderasync_controller.js +14 -0
  54. package/spec/services/integration/server/controllers/mjs_controller.mjs +13 -0
  55. package/spec/services/integration/server/controllers/modeltest_controller.js +18 -0
  56. package/spec/services/integration/server/controllers/websocket_controller.js +13 -0
  57. package/spec/services/integration/server/models/dog.js +6 -0
  58. package/spec/services/integration/server/views/defaultjson/testnojson.ejs +1 -0
  59. package/spec/services/integration/server/views/testreact/testreact.ejs +15 -0
  60. package/spec/services/integration/server.js +9 -0
  61. package/spec/services/integration_sequelize/README.md +5 -0
  62. package/spec/services/integration_sequelize/client/css/styles.css +0 -0
  63. package/spec/services/integration_sequelize/client/download.jpg +0 -0
  64. package/spec/services/integration_sequelize/client/favicon.ico +0 -0
  65. package/spec/services/integration_sequelize/client/index.html +9 -0
  66. package/spec/services/integration_sequelize/client/js/client.js +0 -0
  67. package/spec/services/integration_sequelize/client/js/components/app.jsx +15 -0
  68. package/spec/services/integration_sequelize/config/db.js +4 -0
  69. package/spec/services/integration_sequelize/config/mimes.js +61 -0
  70. package/spec/services/integration_sequelize/config/routes.js +25 -0
  71. package/spec/services/integration_sequelize/config/service.js +48 -0
  72. package/spec/services/integration_sequelize/config/ssl/certificate.pem +22 -0
  73. package/spec/services/integration_sequelize/config/ssl/csr.csr +17 -0
  74. package/spec/services/integration_sequelize/config/ssl/key.pem +28 -0
  75. package/spec/services/integration_sequelize/config/ssl/private_key.pem +30 -0
  76. package/spec/services/integration_sequelize/config/ssl/public_key.pem +9 -0
  77. package/spec/services/integration_sequelize/package.json +23 -0
  78. package/spec/services/integration_sequelize/server/controllers/default_json_controller.js +21 -0
  79. package/spec/services/integration_sequelize/server/controllers/home_controller.js +39 -0
  80. package/spec/services/integration_sequelize/server/models/dog.js +7 -0
  81. package/spec/services/integration_sequelize/server/models/owner.js +10 -0
  82. package/spec/services/integration_sequelize/server/views/defaultjson/testnojson.ejs +1 -0
  83. package/spec/services/integration_sequelize/server/views/testreact/testreact.ejs +15 -0
  84. package/spec/services/integration_sequelize/server.js +9 -0
  85. package/spec/services.integration.spec.js +296 -0
  86. package/spec/services.integration_sequelize.spec.js +60 -0
  87. package/templates/bin/promote.sh +20 -0
  88. package/templates/bin/rollout.sh +74 -0
  89. package/templates/bin/server.js +6 -0
  90. package/templates/bin/start.sh +16 -0
  91. package/templates/common/readme_fetcher.js +4 -0
  92. package/templates/config/db.js +19 -0
  93. package/templates/config/mimes.js +59 -0
  94. package/templates/config/routes.js +38 -0
  95. package/templates/config/service.js +45 -0
  96. package/templates/config/ssl/certificate.pem +22 -0
  97. package/templates/config/ssl/csr.csr +17 -0
  98. package/templates/config/ssl/key.pem +28 -0
  99. package/templates/config/ssl/private_key.pem +30 -0
  100. package/templates/config/ssl/public_key.pem +9 -0
  101. package/templates/default/bin/promote.sh +20 -0
  102. package/templates/default/bin/rollout.sh +74 -0
  103. package/templates/default/bin/server.js +6 -0
  104. package/templates/default/bin/start.sh +16 -0
  105. package/templates/default/common/readme_fetcher.js +4 -0
  106. package/templates/default/config/db.js +19 -0
  107. package/templates/default/config/mimes.js +59 -0
  108. package/templates/default/config/routes.js +38 -0
  109. package/templates/default/config/service.js +45 -0
  110. package/templates/default/config/ssl/certificate.pem +22 -0
  111. package/templates/default/config/ssl/csr.csr +17 -0
  112. package/templates/default/config/ssl/key.pem +28 -0
  113. package/templates/default/config/ssl/private_key.pem +30 -0
  114. package/templates/default/config/ssl/public_key.pem +9 -0
  115. package/templates/default/package-lock.json +9048 -0
  116. package/templates/default/package.json +43 -0
  117. package/templates/default/public/README.xml +332 -0
  118. package/templates/default/public/css/styles.css +17 -0
  119. package/templates/default/public/download.jpg +0 -0
  120. package/templates/default/public/favicon.ico +0 -0
  121. package/templates/default/public/index.html +9 -0
  122. package/templates/default/public/js/client.js +1 -0
  123. package/templates/default/server/controllers/home_controller.js +34 -0
  124. package/templates/default/server/models/User.js +18 -0
  125. package/templates/default/server/views/home/index.ejs +14 -0
  126. package/templates/default/server/views/partials/javascripts.ejs +1 -0
  127. package/templates/default/server/views/partials/reactapp.ejs +1 -0
  128. package/templates/default/server/views/partials/styles.ejs +3 -0
  129. package/templates/default/spec/User.test.js +20 -0
  130. package/templates/default/spec/home_controller.test.js +28 -0
  131. package/templates/default/spec/setupTests.js +0 -0
  132. package/templates/default/src/AboutPage.jsx +9 -0
  133. package/templates/default/src/HomePage.jsx +9 -0
  134. package/templates/default/src/Layout.jsx +78 -0
  135. package/templates/default/src/ReadmePage.jsx +7 -0
  136. package/templates/default/src/app.jsx +29 -0
  137. package/templates/default/src/components/ReadmeLoader.jsx +13 -0
  138. package/templates/default/src/styles/appstyles.css +3 -0
  139. package/templates/default/vite.config.ts +42 -0
  140. package/templates/package-lock.json +9048 -0
  141. package/templates/package.json +43 -0
  142. package/templates/public/README.xml +332 -0
  143. package/templates/public/css/styles.css +17 -0
  144. package/templates/public/download.jpg +0 -0
  145. package/templates/public/favicon.ico +0 -0
  146. package/templates/public/index.html +9 -0
  147. package/templates/public/js/client.js +1 -0
  148. package/templates/server/controllers/home_controller.js +34 -0
  149. package/templates/server/models/User.js +18 -0
  150. package/templates/server/views/home/index.ejs +14 -0
  151. package/templates/server/views/partials/javascripts.ejs +1 -0
  152. package/templates/server/views/partials/reactapp.ejs +1 -0
  153. package/templates/server/views/partials/styles.ejs +3 -0
  154. package/templates/spec/User.test.js +20 -0
  155. package/templates/spec/home_controller.test.js +28 -0
  156. package/templates/spec/setupTests.js +0 -0
  157. package/templates/src/AboutPage.jsx +9 -0
  158. package/templates/src/HomePage.jsx +9 -0
  159. package/templates/src/Layout.jsx +78 -0
  160. package/templates/src/ReadmePage.jsx +7 -0
  161. package/templates/src/app.jsx +29 -0
  162. package/templates/src/components/ReadmeLoader.jsx +13 -0
  163. package/templates/src/styles/appstyles.css +3 -0
  164. package/templates/vite.config.ts +42 -0
package/README.md ADDED
@@ -0,0 +1,419 @@
1
+ # Nails-Boilerplate: A Node Webservice Framework
2
+
3
+ This framework is designed to provide a lightweight, configurable MVC backend
4
+ for node developers. With minimal dependencies, Nails offers a greater
5
+ syntactical familiarity than php alongside the creative freedom of well developed
6
+ server framework solutions like Rails and Django.
7
+
8
+ This boilerplate offers the basic necessities to get your MVC site off the ground.
9
+ The modules used in Nails Boilerplate can be easily extended to produce the custom
10
+ functionality to fit your needs, and you are encouraged to do so.
11
+
12
+ ## Install
13
+
14
+ ```
15
+ sudo npm install -g nails-boilerplate
16
+
17
+ nails init <app_name>
18
+ ```
19
+
20
+ This will initialize a barebones app in the directory of the same name. Take a
21
+ look at the self-documented config files and example controller and view before
22
+ getting started. Additional controllers and views will automatically be imported
23
+ into nails. Now just hook the new controllers in with some new routes and you're
24
+ off to a good start.
25
+
26
+ ```
27
+ cd app_name
28
+
29
+ npm install
30
+
31
+ npm start
32
+ ```
33
+
34
+ ## Getting to know your Nails service
35
+
36
+ For your convenience, here is a quick outline of the main components of a nails service.
37
+ Remember: each object comes with an example file to use for reference when building your service.
38
+
39
+ ### Config
40
+ Your configuration files are stored in app_name/config/. There are three default config files:
41
+
42
+ ```
43
+ service.js
44
+ routes.js
45
+ db.js
46
+ ```
47
+
48
+ Each default config file is annotated with comments documenting each field to
49
+ help you tailor your service to your needs.
50
+
51
+ #### service.js
52
+ service.js contains information necessary to run your server. By default, it
53
+ specifies the port and the location of important libraries.
54
+ ```js
55
+ export default {
56
+ ...
57
+ SERVER_ROOT: "/path/to/my/nails/service",
58
+ PORT: 3333,
59
+ SSL_PORT: 3334,
60
+ }
61
+ ```
62
+
63
+ While most of these values don't need to be changed, feel free to add custom
64
+ fields. The resulting config will be available to your service through the nails
65
+ module:
66
+
67
+ ```js
68
+ import nails from 'nails-boilerplate';
69
+
70
+ const service_config = nails.config
71
+ ```
72
+
73
+ If the config contains a custom field,
74
+
75
+ ```js
76
+ export default {
77
+ ...
78
+ PORT: 3000,
79
+ yourCustomField: 'yourCustomValue'
80
+ }
81
+ ```
82
+
83
+ then `service_config.yourCustomField` as defined above will be equal to
84
+ `'yourCustomValue'`.
85
+
86
+ #### routes.js
87
+ *routes.js* is a list defining mappings from a url path to a *Controller* and
88
+ *Action*. Each entry in the list is an array with three elements:
89
+ `[method, path, options]`
90
+
91
+ **method** is a string defining the HTTP request method of the route. Supported
92
+ methods are *GET*, *PUT*, *POST*, *DELETE*, and *ALL*. All is a special case
93
+ which matches all HTTP request methods. Lastly, *WS* routes will handle WebSocket connections.
94
+
95
+ **path** is a string or regular expression which matches the path of the
96
+ incoming request. If *path* is a string, then the request must match exactly*.
97
+ You can use route parameters to dynamically match parts of the path and assign
98
+ them to the *params* object. For example, if you define a route with the path:
99
+ `'/users/:userId'`
100
+ and your service receives a request with the path:
101
+ `'/users/777'`
102
+ then *userId* will be set in the params object:
103
+ ```js
104
+ { userId: 777 }
105
+ ```
106
+
107
+ You can define *:controller* and *:action* as route parameters as well. Not only
108
+ will those values be set in the params object, but the request will be routed
109
+ to the matching controller and action. See
110
+ [express routes][express_routing_docs] for more information on how route
111
+ parameters work.
112
+
113
+ \*requests for static assets will only match the prefix of the path.
114
+
115
+ **options** is a JSON object which modifies how the request will be routed:
116
+ * *controller* a String indicating the controller to route to.
117
+ * *action* a String indicating the action to route to.
118
+ * *json* a boolean indicating whether to render a JSON response rather than an
119
+ HTML response. If true, nails will not attempt to render a view for this
120
+ route. Instead, your service will respond with JSON for this route.
121
+ * *public* a boolean indicating whether this route is for static assets. If
122
+ true, the router will only attempt to match the prefix of the request path.
123
+ The child portion of the path will be forwarded to the *public/* folder in
124
+ your service directory. For route:
125
+ `['GET', '/public_url_path', {public: true}]`
126
+ if your service receives a request to:
127
+ `/public_url_path/js/index.js`
128
+ then the response will be the file:
129
+ `your_service_root_path/public/js/index.js`
130
+ * *0, 1, 2...* a string which gives regex captures named keys in the params
131
+ object. This will give your regex captures more meaningful named keys rather
132
+ than indices. You can name your regex captures "controller" and/or "action"
133
+ to dynamically route your request to the appropriate handler.
134
+
135
+ #### db.js
136
+
137
+ Quickly configure your database connection here. Nails comes pre-configured to
138
+ use the sequelize connector, giving your models sequelize support. The initial setup
139
+ uses a *sqlite3* database file `config/development.db` and an in-memory database in the test environment. Change the address to change the location and
140
+ version of your desired sql database. Check out [Sequelize](https://sequelize.org)
141
+ for more info.
142
+
143
+ Alternatively, you can configure a connection to MongoDB using the mongoose_connector.js.
144
+ If enabled, models will accept [Mongoose](https://mongoosejs.com/docs/) schemas and will
145
+ be backed by the desired MongoDB. Consider using the in-memory DB during development.
146
+
147
+ ## Controller
148
+
149
+ Controllers are defined in app/controllers/. Each controller module should
150
+ define a Controller subclass. The name will be used to match routes defined in
151
+ config/routes.js for incoming requests. Methods on the controller can be used as
152
+ actions, receiving **params**, **request**, and **response** as arguments.
153
+
154
+ For Example:
155
+ ``` js
156
+ // const Controller = requre("nails-boilerplate").Controller
157
+ import nails from 'nails-boilerplate';
158
+
159
+ class HomeController extends nails.Controller {
160
+ index(params, request, response) {
161
+ // default action
162
+ }
163
+
164
+ signin(params, request, response) {
165
+ // does something then renders a view
166
+ }
167
+ }
168
+ export default HomeController;
169
+
170
+ function helperMethod() {
171
+ // does something but does not have access to response
172
+ }
173
+ ```
174
+
175
+ defines a controller which will match any route to *home#\<action\>*. **index**
176
+ and **signin** are actions which can be used to render a response to the client.
177
+
178
+ ### Local Routes
179
+
180
+ You can define a local routing table directly in the controller.
181
+ Local routes take precidence over global routes. All local routes
182
+ are prefixed with the controller name unless they start with '/'.
183
+ For example, in HomeController the following route:
184
+
185
+ `["get", "data", {action: 'getData', json: true}],`
186
+
187
+ will accept GET requests to /home/data and respond with the json
188
+ object returned by the getData function. If the route is changed to:
189
+
190
+ `["get", "/data", {action: 'getData', json: true}],`
191
+
192
+ it will accept GET requests to /data instead. All local routes are
193
+ implicitly routed to their respective parent controllers.
194
+
195
+ ```js
196
+ export default class UsersController extends nails.Controller {
197
+ routes = [
198
+ // Routes requests to /absolute/path
199
+ ['get', '/absolute/path', {action: 'actionA'}],
200
+ // Routes requests to /users/relative/path
201
+ ['get', './relative/path', {action: 'actionB'}],
202
+ // Routes requests to /users/relative/path
203
+ ['get', 'relative/path', {action: 'actionB'}],
204
+ // If no action is provided, the last path segment
205
+ // is used as the action name.
206
+ ['get', 'actionC']
207
+ ]
208
+
209
+ // Handles requests to /absolute/path
210
+ actionA(request, response, params) {}
211
+
212
+ // Handles requests to /users/relative/path
213
+ actionB(request, response, params) {}
214
+
215
+ // Handles requests to /users/actionC
216
+ actionC(request, response, params) {}
217
+ }
218
+ ```
219
+
220
+ ### Actions
221
+ Actions are used to define how nails should respond to an incoming request.
222
+ If no action has been defined for a route, nails will default to the index
223
+ action.*
224
+
225
+ For example, HomeController#index will attempt to render the view defined in
226
+ //app/views/home/index.jsx
227
+
228
+ The view each action searches for will always follow the pattern:
229
+ //app/views/*\[controller name\]*/*\[action name\]*.jsx
230
+
231
+ The file extension may differ based on which template engine you configure.
232
+
233
+ Depending on the return value, Nails will pass a different set of parameters to
234
+ the view engine:
235
+ * **undefined** If there is no return statement in the action, Nails will pass
236
+ the *params* obect to the rendering engine.
237
+ * **Object** If a generic object is returned, Nails will attempt to autorender
238
+ the view immediately using the returned object instead of *params*.**
239
+ * **Promise** If a promise is returned, Nails will wait to autorender the view
240
+ until the *Promise* resolves. If it resolves with no return value, the view
241
+ is rendered using *params*. Otherwise, the view is rendered using the
242
+ resolved value of the *Promise*\**
243
+
244
+ \*If a response has already been sent to the client, autorender will be skipped.
245
+ \*\*For JSON routes, the returned object will be rendered as stringified JSON.
246
+
247
+ #### Params
248
+ Params is a generic JSON object which represents the request details. Usually,
249
+ Params will correspond to the query portion of your request.
250
+
251
+ For example, a GET request to *//some/path?item0=a&item1=b* will generate the
252
+ params object:
253
+ ``` js
254
+ {
255
+ item0: "a",
256
+ item1: "b"
257
+ }
258
+ ```
259
+
260
+ #### Request
261
+ An [express Request object][express_request_docs].
262
+
263
+ #### Response
264
+ The response object provided by *express.js*. The *#render()* method has been
265
+ overridden to allow for the rendering of views by name.
266
+
267
+ #### JSON Actions
268
+ JSON actions only return JSON objects in the response instead of text or HTML. These actions are ideal for building an API Server. There are three ways to designate JSON Actions:
269
+
270
+ ##### Express Response Object
271
+ ```js
272
+ response.json({your: 'jsonresponse'})
273
+ ```
274
+ Simply use the express Response object directly
275
+
276
+ ##### JSON Routes
277
+ You can configure an individual route to respond with JSON by setting the `json` option to `true`.
278
+ ```js
279
+ ['get', '/your/json/route', {json: true}],
280
+ ```
281
+
282
+ ##### API Controllers
283
+ By setting `json` to `true`, all actions in a controller will respond with JSON.
284
+
285
+ ```js
286
+ class YourApiController extends nails.Controller {
287
+ json = true;
288
+
289
+ action(params, request, response) {
290
+ return {your: 'jsonresponse'};
291
+ }
292
+ }
293
+ ```
294
+
295
+ ## Model
296
+
297
+ Models are programmatic representations of data you wish to persist in a
298
+ database. The constructor for Model accepts two arguments: the `modelName` and an
299
+ `options` object which is passed to the database connector module.
300
+
301
+ ### Sequelize Models
302
+
303
+ Sequelize models are subclasses of
304
+ [Sequelize Models][sequelize_model_docs], and come with the `count()`, `findAll()`,
305
+ and `create()` methods, to name a few. You can define your own models by
306
+ extending an instance of the `Model` class provided by Nails:
307
+
308
+ ```js
309
+ // const Model = require("nails-boilerplate").Model;
310
+ import nails from 'nails-boilerplate';
311
+ import {DataTypes} from 'sequelize';
312
+ schema = {
313
+ name: {type: DataTypes.STRING, allowNull: false},
314
+ email: {type: DataTypes.STRING, allowNull: false}
315
+ };
316
+
317
+ options = {
318
+ indexes: [
319
+ {
320
+ unique: true,
321
+ fields: ['email'],
322
+ },
323
+ ],
324
+ };
325
+
326
+ export default class User extends new Model("User", {schema, options}) {
327
+ someHelperMethod() {
328
+ // This method will be available on all instances of User and is
329
+ // an ideal way to simplify data manipulation.
330
+ }
331
+ };
332
+
333
+ ```
334
+
335
+ ### Mongoose Models
336
+ Mongoose models are subclasses of
337
+ [Mongoose Models][mongoose_model_docs], and come with the `save()`, `find()`,
338
+ and `where()` methods, to name a few. You can define your own models by
339
+ extending an instance of the `Model` class provided by Nails:
340
+
341
+ ``` js
342
+ // const Model = require("nails-boilerplate").Model;
343
+ import nails from 'nails-boilerplate';
344
+ const userSchema = {name: String, email: String};
345
+ export default class User extends new Model("User", {schema: userSchema}) {
346
+ // Define your helper methods here
347
+ };
348
+ ```
349
+
350
+ The `schema` option for Mongoose Models accepts a schema field that is used
351
+ to define how documents are stored in MongoDB.
352
+
353
+ ### Model Library
354
+ Nails will store all instantialized models in a single object called `MODELS`. By accessing these models via the library, you can avoid circular dependencies and ensure all models have been fully initialized.
355
+
356
+ ```js
357
+ class User extends nails.Model("User", {schema, options}) {
358
+ // A helper method which depends on anoher model using the
359
+ // Nails Model Library rather than directly importing the model.
360
+ async findFriends() {
361
+ return nails.MODELS.Friend.findByUserId(this.id);
362
+ }
363
+ }
364
+ ```
365
+ This design pattern is not always necessary, but will help avoid circular dependencies.
366
+
367
+ ### Database Connectors
368
+
369
+ Database connectors are intermediaries which define how a Model interacts with
370
+ a database. Database connector modules need to export two methods:
371
+ * _connect(db_config)_ uses the db config defined in *db.js* to connect to
372
+ a database. This function will be called once by Nails.
373
+ * _generateModelSuperclass(name, options)_ uses the provided Model name and
374
+ options to generate a Model prototype for use as an interface. A Model
375
+ interface is generated for each of your models, allowing them to interact with
376
+ a database. Ideally, interfaces will define save() and find() methods, but
377
+ these methods and their implementations are up to the individual connector.
378
+
379
+ ## View
380
+ Views are dynamic templates used to render an html response for a browser.
381
+ Nails comes prepackaged with EJS templates.
382
+ If no template engine is specified in the service config, Nails will default to
383
+ EJS. Nails will always attempt to autorender your views unless a response has
384
+ already been sent to the client.
385
+
386
+ ### React Frontend with Vite
387
+
388
+ This project uses Vite to build the frontend React application. The source files for the React app are located in the `src` directory. The server is pre-configured to serve the built React application.
389
+
390
+ To rebuild the frontend application, you can run the following command:
391
+
392
+ ```bash
393
+ npm run build
394
+ ```
395
+
396
+ ## Testing
397
+
398
+ This project uses [Vitest](https://vitest.dev/) for running tests. All test files are located in the `spec` folder. To run the tests, use the following command:
399
+
400
+ ```bash
401
+ npm test
402
+ ```
403
+
404
+ For more information on how to write tests with Vitest, please refer to the [official documentation](https://vitest.dev/guide/).
405
+
406
+ Stay tuned as nails evolves:
407
+
408
+ * Server/client redirects
409
+ * Custom Request middleware
410
+ * Fancy Logging
411
+ * Sessions
412
+ * Server security
413
+
414
+ Enjoy! Feature requests, bug reports, and comments are welcome on github.
415
+
416
+ [express_routing_docs]: https://expressjs.com/en/guide/routing.html
417
+ [express_request_docs]: https://expressjs.com/en/5x/api.html#req
418
+ [mongoose_model_docs]: https://mongoosejs.com/docs/api/model.html
419
+ [sequelize_model_docs]: https://sequelize.org/docs/v6/core-concepts/model-basics/
@@ -0,0 +1,103 @@
1
+ // Install script which adds commands for running nails
2
+ // to the path
3
+ import path from 'node:path';
4
+ import fs from 'node:fs';
5
+ import {copySync} from 'fs-extra/esm';
6
+ import {exec} from 'child_process';
7
+ // var exec = require('child_process').exec;
8
+ var args = process.argv.slice(2);
9
+
10
+ var appName = args[0];
11
+ var originalDir = process.cwd();
12
+ var lastDir;
13
+ var notRoot = true;
14
+
15
+ // Make sure an app name was passed
16
+ if ( !appName ) {
17
+ console.log("Missing argument for the application name");
18
+ process.exit(1);
19
+ }
20
+
21
+ // check to make sure this is not already a nails app directory
22
+ if ( isNailsApp(originalDir) ) {
23
+ console.log("You are already in a Nails Application");
24
+ process.exit(1);
25
+ }
26
+
27
+ createApp(appName);
28
+
29
+ function isNailsApp( originalDir, directory ) {
30
+ var dir = directory || originalDir;
31
+ process.chdir('..');
32
+ var nextDir = process.cwd();
33
+ var files = fs.readdirSync(dir);
34
+ var nailsAppRootHere = files.indexOf('NAILS') >= 0;
35
+
36
+ if (nextDir == dir) {
37
+ process.chdir(originalDir);
38
+ return nailsAppRootHere;
39
+ } else {
40
+ return nailsAppRootHere || isNailsApp( originalDir, nextDir );
41
+ }
42
+ }
43
+
44
+ function createApp( name ) {
45
+ const templateStyle = 'default'
46
+ var templateRoot = path.resolve(import.meta.dirname, `../../templates/${templateStyle}`);
47
+ if (!fs.existsSync(name)) fs.mkdirSync(name);
48
+ fs.open(name + '/NAILS','w', 0o666, function(err, fd) {
49
+ if (err) throw err;
50
+ fs.writeFileSync(name + '/NAILS', '/* This marks the root of the NAILS app */');
51
+ fs.closeSync(fd);
52
+
53
+ copySync(path.resolve(templateRoot, './server'), name + '/server');
54
+ copySync(path.resolve(templateRoot, './src'), name + '/src');
55
+ copySync(path.resolve(templateRoot, './public'), name + '/public');
56
+ copySync(path.resolve(templateRoot, './config'), name + '/config');
57
+ copySync(path.resolve(templateRoot, './common'), name + '/common');
58
+ copySync(path.resolve(templateRoot, './spec'), name + '/spec');
59
+ copySync(path.resolve(templateRoot, './bin'), name + '/bin');
60
+
61
+ checkWrites();
62
+ });
63
+
64
+ //fs.copyFileSync(templateRoot + '/.babelrc', name + '/.babelrc');
65
+ fs.copyFileSync(path.resolve(templateRoot, './vite.config.ts'), name + '/vite.config.ts');
66
+
67
+ // fs.open(name + 'bin/server.js','w', 0o666, function(err, fd) {
68
+ // if (err) throw err;
69
+ // fs.readFile(path.resolve(templateRoot,'./bin/server.js'), 'utf8', function(err, data) {
70
+ // if (err) throw err;
71
+ // fs.writeFileSync( name + '/server.js', data );
72
+ // fs.closeSync(fd);
73
+ // checkWrites();
74
+ // });
75
+ // });
76
+
77
+ //TODO: use toJSON to dynamically create package.json
78
+ //TODO: install dependencies after writing package.json
79
+ fs.open(name + '/package.json','w', 0o666, function(err, fd) {
80
+ if (err) throw err;
81
+ fs.readFile(templateRoot + '/package.json', 'utf8', function(err, data) {
82
+ if (err) throw err;
83
+ fs.writeFileSync( name + '/package.json', data.replace('nails_app', name) );
84
+ fs.closeSync(fd);
85
+ checkWrites();
86
+ });
87
+ });
88
+ }
89
+
90
+ var numWrites = 0;
91
+ function checkWrites() {
92
+ numWrites++;
93
+ if (numWrites == 2) {
94
+ console.log("Initialized new Nails Application successfully");
95
+ console.log("installing nails locally");
96
+ // change into app directory
97
+ process.chdir(appName);
98
+
99
+ // change back to original directory
100
+ process.chdir('..');
101
+ process.exit(0);
102
+ }
103
+ }
@@ -0,0 +1,81 @@
1
+ /* The entry point for nails console commands */
2
+ #include <iostream>
3
+ #include <stdio.h>
4
+ #include <stdlib.h>
5
+ #include <cstring>
6
+
7
+ /**
8
+ * Helper method for trimming the whitespace from strings created from command line output
9
+ */
10
+ char *trimwhitespace(char *str) {
11
+ char *end;
12
+
13
+ // Trim leading space
14
+ while(isspace(*str)) str++;
15
+
16
+ if(*str == 0) // All spaces?
17
+ return str;
18
+
19
+ // Trim trailing space
20
+ end = str + strlen(str) - 1;
21
+ while(end > str && isspace(*end)) end--;
22
+
23
+ // Write new null terminator
24
+ *(end+1) = 0;
25
+
26
+ return str;
27
+ }
28
+
29
+ int main( int argc, char *argv[] ) {
30
+ if ( argc < 2 ) {
31
+ std::cout << "Not enough arguments" << std::endl;
32
+ } else {
33
+ //system("echo 'system call'");
34
+ FILE *fp;
35
+ int status;
36
+ char path[1035];
37
+ char *quote = "'";
38
+ char args[500];
39
+
40
+ /* concatenate the args to be passed to nails.js script */
41
+ std::strcpy( args, quote );
42
+ for (int i = 1; i < argc; i++) {
43
+ // TODO: refractor this into a function dedicated to processing the additiional arguments
44
+ if ( i > 1 ) {
45
+ std::strcat( args, " " );
46
+ }
47
+ std::strcat( args, argv[i] );
48
+ }
49
+ std::strcat( args, quote );
50
+
51
+ /* Open the command for reading. */
52
+ fp = popen("npm root -g", "r");
53
+ if (fp == NULL) {
54
+ printf( "Failed to run command\n" );
55
+ return -1;
56
+ }
57
+
58
+ /* Read the output line */
59
+ while (fgets(path, sizeof(path)-1, fp) != NULL) {
60
+ char commandPre[] = "node ";
61
+ char comPathSuf[] = "/nails-boilerplate/bin/lib/nails.js ";
62
+
63
+ // Concatenate the strings into a command to run the nails script
64
+ int strLen = std::strlen(commandPre) + std::strlen(path) + std::strlen(comPathSuf) + std::strlen(args) + 1;
65
+ char command[strLen];
66
+ std::strcpy(command, commandPre);
67
+ std::strcat(command, trimwhitespace(path));
68
+ std::strcat(command, comPathSuf);
69
+ std::strcat(command, args);
70
+ /* print for testing only */
71
+ //printf("%s", command);
72
+ // commented out for testing
73
+ system(command);
74
+ }
75
+
76
+ /* close */
77
+ pclose(fp);
78
+
79
+ return 0;
80
+ }
81
+ }
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+
3
+ // The entry point for the nails executable
4
+ import { exec } from 'child_process';
5
+ // var exec = require('child_process').exec;
6
+ console.log('nailsargs are: ', process.argv);
7
+ var args = process.argv.splice(2);
8
+
9
+ // TODO: print version and help info if called with no arguments
10
+
11
+ var command = 'node ' + import.meta.dirname + '/' + args[0] + '.js';
12
+
13
+ for (var i = 1; i < args.length; i++) {
14
+ command = command + " " + args[i];
15
+ }
16
+
17
+ //TODO can use require for this
18
+ exec( command, function(error, stdout, stderr) {
19
+ console.log(stdout);
20
+ if (error) {
21
+ console.log(error);
22
+ }
23
+ });
@@ -0,0 +1,4 @@
1
+ // a test script for npm bin functionality
2
+ console.log('This is just a test. If you see this message, everything is working.');
3
+ var args = process.argv.slice(10);
4
+ console.log("the arguments are: ", args);
package/index.js ADDED
@@ -0,0 +1,6 @@
1
+ // Sets the Nails global object
2
+ import nails from './lib/nails.js';
3
+
4
+ export default nails;
5
+
6
+ // set up Nails here
@@ -0,0 +1,24 @@
1
+ // const bodyParser = require('body-parser');
2
+ import pkg from 'body-parser';
3
+ const {urlencoded, json} = pkg;
4
+ import express from 'express';
5
+ import expressWs from 'express-ws';
6
+ import cookieParser from 'cookie-parser';
7
+
8
+ /**
9
+ * Singleton express application.
10
+ */
11
+ // var express = require('express');
12
+ var app = express();
13
+ app.use(cookieParser());
14
+ // TODO: this has to be done before routes in order to work. Can consider allowing the config to turn this off
15
+ // expressWs(app);
16
+ // Parse application/x-www-form-urlencoded
17
+ app.use(urlencoded({ limit: '2mb', extended: false }));
18
+ // Parse application/json
19
+ app.use(json({limit: '2mb'}));
20
+
21
+ app.Router = express.Router;
22
+ app.static = express.static;
23
+
24
+ export default app;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * The collection is an object descending from the array class.
3
+ * It comes with methods for persising groups of models to the database.
4
+ */
5
+ export default function Collection() {
6
+ }