pinstripe 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/lib/commands/generate_project.js +18 -0
  2. package/lib/commands/list_commands.js +2 -4
  3. package/lib/commands/list_migrations.js +2 -4
  4. package/lib/commands/list_models.js +2 -4
  5. package/lib/commands/list_node_wrappers.js +2 -4
  6. package/lib/commands/list_services.js +2 -4
  7. package/lib/commands/list_views.js +2 -4
  8. package/lib/database/row.js +2 -1
  9. package/lib/database/table.js +2 -1
  10. package/lib/initialize.client.js +4 -27
  11. package/lib/node_wrapper.js +52 -3
  12. package/lib/node_wrappers/anchor.client.js +2 -0
  13. package/lib/node_wrappers/form.client.js +12 -1
  14. package/lib/node_wrappers/markdown_editor.client.js +7 -0
  15. package/lib/node_wrappers/overlay.client.js +13 -0
  16. package/lib/services/command_names.js +6 -0
  17. package/lib/services/config.js +0 -1
  18. package/lib/services/migration_names.js +6 -0
  19. package/lib/services/model_names.js +6 -0
  20. package/lib/services/node_wrapper_names.js +6 -0
  21. package/lib/services/render_form.js +66 -58
  22. package/lib/services/render_table.js +6 -5
  23. package/lib/services/send_mail.js +32 -0
  24. package/lib/services/service_names.js +6 -0
  25. package/lib/services/stylesheet_view_names.js +10 -0
  26. package/lib/services/view_names.js +6 -0
  27. package/lib/views/_layout.js +4 -2
  28. package/lib/views/sign_in.js +0 -5
  29. package/lib/views/stylesheets/components/button.css.js +56 -0
  30. package/lib/views/stylesheets/components/card.css.js +41 -0
  31. package/lib/views/stylesheets/components/form.css.js +11 -0
  32. package/lib/views/stylesheets/components/frame.css.js +10 -0
  33. package/lib/views/stylesheets/components/input.css.js +54 -0
  34. package/lib/views/stylesheets/components/label.css.js +14 -0
  35. package/lib/views/stylesheets/components/markdown_editor.css.js +53 -0
  36. package/lib/views/stylesheets/components/modal.css.js +77 -0
  37. package/lib/views/stylesheets/components/overlay.css.js +17 -0
  38. package/lib/views/stylesheets/components/pagination.css.js +80 -0
  39. package/lib/views/stylesheets/components/progress_bar.css.js +32 -0
  40. package/lib/views/stylesheets/components/textarea.css.js +17 -0
  41. package/lib/views/stylesheets/global.css +120 -0
  42. package/lib/views/stylesheets/reset.css +74 -0
  43. package/lib/views/stylesheets/vars.css +25 -0
  44. package/package.json +3 -2
  45. package/pinstripe_development.db +0 -0
  46. package/lib/views/base.css.js +0 -406
@@ -36,11 +36,29 @@ export default async ({
36
36
  await generateFile(`lib/index.js`, () => {
37
37
  line();
38
38
  line(`import { importAll } from 'pinstripe';`);
39
+ dependencies.forEach((dependency) => {
40
+ if(dependency != 'pinstripe') line(`import '${dependency}';`);
41
+ });
39
42
  line();
40
43
  line(`importAll(import.meta.url);`);
41
44
  line();
42
45
  });
43
46
 
47
+ await generateFile(`README.md`, () => {
48
+ line();
49
+ line(`# ${name}`);
50
+ line();
51
+ line('## Getting started');
52
+ line();
53
+ line('```bash');
54
+ indent(() => {
55
+ line('pinstripe init-database');
56
+ line('pinstripe start-server');
57
+ });
58
+ line('```');
59
+ line();
60
+ });
61
+
44
62
  spawnSync('yarn', [ 'add', ...dependencies ], {
45
63
  stdio: 'inherit'
46
64
  });
@@ -1,13 +1,11 @@
1
1
 
2
2
  import chalk from 'chalk';
3
3
 
4
- import { Command } from '../command.js';
5
-
6
- export default () => {
4
+ export default ({ commandNames }) => {
7
5
  console.log('');
8
6
  console.log('The following commands are available:');
9
7
  console.log('');
10
- Object.keys(Command.classes).sort().forEach(commandName => {
8
+ commandNames.forEach(commandName => {
11
9
  console.log(` * ${chalk.green(commandName)}`);
12
10
  });
13
11
  console.log('');
@@ -1,13 +1,11 @@
1
1
 
2
2
  import chalk from 'chalk';
3
3
 
4
- import { Migration } from '../database/migration.js'
5
-
6
- export default () => {
4
+ export default ({ migrationNames }) => {
7
5
  console.log('');
8
6
  console.log('The following migrations are available:');
9
7
  console.log('');
10
- Object.keys(Migration.classes).sort().forEach(migrationName => {
8
+ migrationNames.forEach(migrationName => {
11
9
  console.log(` * ${chalk.green(migrationName)}`);
12
10
  });
13
11
  console.log('');
@@ -1,13 +1,11 @@
1
1
 
2
2
  import chalk from 'chalk';
3
3
 
4
- import { Row } from '../database/row.js';
5
-
6
- export default () => {
4
+ export default ({ modelNames }) => {
7
5
  console.log('');
8
6
  console.log('The following models are available:');
9
7
  console.log('');
10
- Object.keys(Row.classes).sort().forEach(modelName => {
8
+ modelNames.forEach(modelName => {
11
9
  console.log(` * ${chalk.green(modelName)}`);
12
10
  });
13
11
  console.log('');
@@ -1,13 +1,11 @@
1
1
 
2
2
  import chalk from 'chalk';
3
3
 
4
- import { NodeWrapper } from '../node_wrapper.js';
5
-
6
- export default () => {
4
+ export default ({ nodeWrapperNames }) => {
7
5
  console.log('');
8
6
  console.log('The following node wrappers are available:');
9
7
  console.log('');
10
- Object.keys(NodeWrapper.classes).sort().forEach(nodeWrapperName => {
8
+ nodeWrapperNames.forEach(nodeWrapperName => {
11
9
  console.log(` * ${chalk.green(nodeWrapperName)}`);
12
10
  });
13
11
  console.log('');
@@ -1,13 +1,11 @@
1
1
 
2
2
  import chalk from 'chalk';
3
3
 
4
- import { ServiceFactory } from '../service_factory.js';
5
-
6
- export default () => {
4
+ export default ({ serviceNames }) => {
7
5
  console.log('');
8
6
  console.log('The following services are available:');
9
7
  console.log('');
10
- Object.keys(ServiceFactory.classes).sort().forEach(serviceName => {
8
+ serviceNames.forEach(serviceName => {
11
9
  console.log(` * ${chalk.green(serviceName)}`);
12
10
  });
13
11
  console.log('');
@@ -1,13 +1,11 @@
1
1
 
2
2
  import chalk from 'chalk';
3
3
 
4
- import { View } from '../view.js';
5
-
6
- export default () => {
4
+ export default ({ viewNames }) => {
7
5
  console.log('');
8
6
  console.log('The following views are available:');
9
7
  console.log('');
10
- Object.keys(View.classes).sort().forEach(viewName => {
8
+ viewNames.forEach(viewName => {
11
9
  console.log(` * ${chalk.green(viewName)}`);
12
10
  });
13
11
  console.log('');
@@ -175,9 +175,10 @@ export const Row = Base.extend().include({
175
175
 
176
176
  const submitTitle = 'Save Changes';
177
177
  const cancelTitle = 'Cancel';
178
+ const unsavedChangesConfirm = 'There are unsaved changes are you sure you want to close?';
178
179
 
179
180
  return {
180
- title, fields, submitTitle, cancelTitle,
181
+ title, fields, submitTitle, cancelTitle, unsavedChangesConfirm,
181
182
 
182
183
  submit: async (values, success) => {
183
184
  return success(await this.update(values));
@@ -227,9 +227,10 @@ export const Table = Base.extend().include({
227
227
 
228
228
  const submitTitle = title;
229
229
  const cancelTitle = 'Cancel';
230
+ const unsavedChangesConfirm = 'There are unsaved changes are you sure you want to close?';
230
231
 
231
232
  return {
232
- title, fields, submitTitle, cancelTitle,
233
+ title, fields, submitTitle, cancelTitle, unsavedChangesConfirm,
233
234
 
234
235
  submit: async (values, success) => {
235
236
  return success(await this.insert(values));
@@ -2,33 +2,10 @@
2
2
  import { NodeWrapper } from './node_wrapper.js';
3
3
 
4
4
  if(typeof window != 'undefined'){
5
- let ready = false;
6
-
7
- function initializeTree(node){
8
- NodeWrapper.instanceFor(node).descendants
9
- }
10
-
11
- const observer = new MutationObserver(mutations => {
12
- if(ready){
13
- mutations.forEach(
14
- mutation => {
15
- mutation.addedNodes.forEach(node => initializeTree(node));
16
- }
17
- )
18
- }
19
- })
20
-
21
- observer.observe(document.documentElement, {
22
- attributes: false,
23
- childList: true,
24
- subtree: true
5
+ window.addEventListener('DOMContentLoaded', (event) => {
6
+ const documentNodeWrapper = NodeWrapper.instanceFor(document);
7
+ documentNodeWrapper.observe({ add: true }, nodeWrapper => nodeWrapper.descendants);
8
+ documentNodeWrapper.patch(document.documentElement.outerHTML);
25
9
  });
26
-
27
- setTimeout(() => {
28
- ready = true
29
- NodeWrapper.instanceFor(document).patch(
30
- document.documentElement.outerHTML
31
- );
32
- }, 0);
33
10
  }
34
11
 
@@ -45,6 +45,7 @@ export const NodeWrapper = Base.extend().include({
45
45
  initialize(node, skipInit = false){
46
46
  this.node = node;
47
47
  this._registeredEventListeners = [];
48
+ this._registeredObservers = [];
48
49
  this._registeredTimers = [];
49
50
  this._virtualNodeFilters = [];
50
51
 
@@ -54,6 +55,12 @@ export const NodeWrapper = Base.extend().include({
54
55
  this.traverse(normalizeVirtualNode);
55
56
  });
56
57
 
58
+ const { autofocus } = this.attributes;
59
+
60
+ if(autofocus){
61
+ this.setTimeout(() => this.node.focus());
62
+ }
63
+
57
64
  const { autosubmit, trigger } = this.data;
58
65
 
59
66
  if(autosubmit){
@@ -375,7 +382,45 @@ export const NodeWrapper = Base.extend().include({
375
382
  addVirtualNodeFilter(fn){
376
383
  this._virtualNodeFilters.push(fn);
377
384
  return this;
378
- }
385
+ },
386
+
387
+ observe: overload({
388
+ ['object, function'](options, fn){
389
+ const { add = false, remove = false, alter = false } = options;
390
+
391
+ const observer = new MutationObserver(mutations => {
392
+ mutations.forEach(
393
+ mutation => {
394
+ if(mutation.type == 'childList'){
395
+ if(add) mutation.addedNodes.forEach(node => fn(NodeWrapper.instanceFor(node), 'add'));
396
+ if(remove) mutation.removedNodes.forEach(node => fn(NodeWrapper.instanceFor(node), 'remove'));
397
+ }
398
+ if(mutation.type == 'attributes' && alter){
399
+ fn(NodeWrapper.instanceFor(mutation.target), 'alter', mutation.attributeName);
400
+ }
401
+ if(mutation.type == 'characterData' && alter){
402
+ fn(NodeWrapper.instanceFor(mutation.target), 'alter', 'value');
403
+ }
404
+ }
405
+ )
406
+ });
407
+
408
+ observer.observe(this.node, {
409
+ attributes: alter,
410
+ characterData: 'alter',
411
+ childList: add || remove,
412
+ subtree: true
413
+ });
414
+
415
+ this._registeredObservers.push(observer);
416
+
417
+ return this;
418
+ },
419
+
420
+ function(fn){
421
+ return this.observe({ add: true, remove: true, alter: true }, fn);
422
+ }
423
+ })
379
424
  });
380
425
 
381
426
  function cleanChildren(){
@@ -391,6 +436,10 @@ function clean(){
391
436
  this.node.removeEventListener(...this._registeredEventListeners.pop());
392
437
  }
393
438
 
439
+ while(this._registeredObservers.length){
440
+ this._registeredObservers.pop().disconnect();
441
+ }
442
+
394
443
  clearTimers.call(this);
395
444
 
396
445
  if(this._overlayChild) this._overlayChild.remove();
@@ -427,8 +476,8 @@ function createVirtualNode(html){
427
476
  }
428
477
 
429
478
  function patch(attributes, virtualChildren){
430
- if(this.is('.progress-bar')) return;
431
- const isEmptyFrame = this.is('.frame') && virtualChildren.length == 0;
479
+ if(this.is('.ps-progress-bar')) return;
480
+ const isEmptyFrame = this.is('.ps-frame') && virtualChildren.length == 0;
432
481
  if(isEmptyFrame && attributes['data-load-on-init'] === undefined){
433
482
  attributes['data-load-on-init'] = 'true';
434
483
  }
@@ -14,5 +14,7 @@ export default {
14
14
  if(action == 'load') loadFrame.call(this, confirm, target, method, href);
15
15
  if(action == 'remove') removeFrame.call(this, confirm, target);
16
16
  });
17
+
18
+ if(this.is('input, textarea')) this.on('keyup', (event) => this.trigger('click'));
17
19
  }
18
20
  };
@@ -6,11 +6,22 @@ export default {
6
6
  initialize(...args){
7
7
  this.constructor.parent.prototype.initialize.call(this, ...args);
8
8
 
9
+ const { confirm, target = '_self', method = 'GET', action } = { ...this.attributes, ...this.data };
10
+
9
11
  this.on('submit', (event) => {
10
12
  event.preventDefault();
11
13
  event.stopPropagation();
12
- const { confirm, target = '_self', method = 'GET', action } = { ...this.attributes, ...this.data };
14
+
13
15
  loadFrame.call(this, confirm, target, method, action);
14
16
  });
17
+
18
+ this._initialHash = JSON.stringify(this.values);
19
+ },
20
+
21
+ isForm: true,
22
+
23
+ get hasUnsavedChanges(){
24
+ return this.data.hasUnsavedChanges || JSON.stringify(this.values) != this._initialHash;
15
25
  }
26
+
16
27
  };
@@ -13,6 +13,13 @@ export default {
13
13
 
14
14
  const previewFrame = this.frame.descendants.find(n => n.is('.ps-markdown-editor-preview-pane'));
15
15
 
16
+ previewFrame.observe((current) => {
17
+ while(current.parent && current.parent != previewFrame) {
18
+ current = current.parent;
19
+ }
20
+ if(current.node.scrollIntoView) current.node.scrollIntoView({behavior: "smooth", block: "start", inline: "nearest"});
21
+ })
22
+
16
23
  this.on('submit', () => {
17
24
  const { value } = this.values;
18
25
  previewFrame.load({ _method: 'post', value });
@@ -16,7 +16,20 @@ export default {
16
16
  },
17
17
 
18
18
  remove(...args){
19
+ if(window.getSelection().type == 'Range') return;
20
+
21
+ let canRemove = true;
22
+ this.descendants.filter(n => n.isForm).forEach(({ hasUnsavedChanges, data: { unsavedChangesConfirm } }) => {
23
+ if(hasUnsavedChanges && unsavedChangesConfirm && !confirm(unsavedChangesConfirm)){
24
+ canRemove = false;
25
+ }
26
+ })
27
+ if(!canRemove) return;
28
+
19
29
  this.constructor.parent.prototype.remove.call(this, ...args);
30
+
31
+ delete this.parent._overlayChild;
32
+
20
33
  if(!this.document.descendants.find(node => node.is('body')).children.filter((child) => child.is('.ps-overlay')).length){
21
34
  this.document.descendants.filter(node => node.is('html')).forEach(node => {
22
35
  node.removeClass('ps-has-overlay');
@@ -0,0 +1,6 @@
1
+
2
+ import { Command } from '../command.js';
3
+
4
+ export default () => {
5
+ return Object.keys(Command.classes).sort();
6
+ };
@@ -1,6 +1,5 @@
1
1
 
2
2
  export default async ({ project }) => {
3
-
4
3
  if(process.env.DATABASE_ADAPTER == 'mysql'){
5
4
  return {
6
5
  database: {
@@ -0,0 +1,6 @@
1
+
2
+ import { Migration } from '../database/migration.js'
3
+
4
+ export default () => {
5
+ return Object.keys(Migration.classes).sort();
6
+ };
@@ -0,0 +1,6 @@
1
+
2
+ import { Row } from '../database/row.js';
3
+
4
+ export default () => {
5
+ return Object.keys(Row.classes).sort();
6
+ };
@@ -0,0 +1,6 @@
1
+
2
+ import { NodeWrapper } from '../node_wrapper.js';
3
+
4
+ export default () => {
5
+ return Object.keys(NodeWrapper.classes).sort();
6
+ };
@@ -50,6 +50,7 @@ export default {
50
50
  const fields = extractFields(formAdapter, options);
51
51
  const submitTitle = options.submitTitle || formAdapter.submitTitle;
52
52
  const cancelTitle = options.cancelTitle || formAdapter.cancelTitle;
53
+ const unsavedChangesConfirm = options.unsavedChangesConfirm || formAdapter.unsavedChangesConfirm;
53
54
 
54
55
  const indexedFields = indexFieldsByName(fields);
55
56
  const otherErrors = [];
@@ -72,67 +73,74 @@ export default {
72
73
  return renderHtml`
73
74
  <div class="ps-modal" data-node-wrapper="anchor" data-action="remove" data-ignore-events-from-children="true">
74
75
  <button data-node-wrapper="anchor" data-action="remove"></button>
75
- <form method="post" enctype="multipart/form-data" autocomplete="off">
76
- <header>
77
- <p>${title}</p>
78
- </header>
79
- <section>
80
- ${() => {
81
- if(otherErrors.length){
76
+ <form
77
+ class="ps-card"
78
+ method="post"
79
+ enctype="multipart/form-data"
80
+ autocomplete="off"
81
+ ${unsavedChangesConfirm ? renderHtml`data-unsaved-changes-confirm="${unsavedChangesConfirm}"` : undefined}
82
+ ${unsavedChangesConfirm && params._method == 'POST' ? renderHtml`data-has-unsaved-changes="true"` : undefined}
83
+ >
84
+ <div class="ps-card-header">
85
+ <p class="ps-card-header-title">${title}</p>
86
+ </div>
87
+ <div class="ps-card-body">
88
+ ${() => {
89
+ if(otherErrors.length){
90
+ return renderHtml`
91
+ <div class="ps-field">
92
+ ${otherErrors.map(error => renderHtml`
93
+ <p class="ps-is-error">${error}</p>
94
+ `)}
95
+ </div>
96
+ `
97
+ }
98
+ }}
99
+ ${fields.map(({ label, name, type, value, error }) => {
100
+ if(type == 'hidden'){
101
+ return renderHtml`
102
+ <input type="hidden" name="${name}" value="${value}">
103
+ `;
104
+ }
82
105
  return renderHtml`
83
- <div class="ps-field">
84
- ${otherErrors.map(error => renderHtml`
85
- <p class="ps-is-error">${error}</p>
86
- `)}
106
+ <div>
107
+ <label class="ps-label">${label}</label>
108
+ ${() => {
109
+ if(type == 'textarea'){
110
+ return renderHtml`
111
+ <textarea class="ps-textarea${error ? ' ps-is-error' : ''}" name="${name}">${value}</textarea>
112
+ `
113
+ }
114
+ if(type == 'markdown'){
115
+ return renderHtml`
116
+ <textarea class="ps-textarea${error ? ' ps-is-error' : ''}" name="${name}" data-node-wrapper="anchor" data-target="_overlay" data-href="&_part=markdown-editor">${value}</textarea>
117
+ `
118
+ }
119
+ if(type == 'checkbox'){
120
+ return renderHtml`
121
+ <input class="ps-input${error ? ' ps-is-error' : ''}" type="checkbox" name="${name}" type="${type}" ${value ? 'checked' : ''} />
122
+ `
123
+ }
124
+ return renderHtml`
125
+ <input class="ps-input${error ? ' ps-is-error' : ''}" name="${name}" type="${type}" value="${value}">
126
+ `
127
+ }}
128
+ ${() => {
129
+ if(error){
130
+ return renderHtml`
131
+ <p class="ps-is-error">${error}</p>
132
+ `
133
+ }
134
+ }}
87
135
  </div>
88
- `
89
- }
90
- }}
91
- ${fields.map(({ label, name, type, value, error }) => {
92
- if(type == 'hidden'){
93
- return renderHtml`
94
- <input type="hidden" name="${name}" value="${value}">
95
136
  `;
96
- }
97
- return renderHtml`
98
- <div>
99
- <label class="ps-label">${label}</label>
100
- ${() => {
101
- if(type == 'textarea'){
102
- return renderHtml`
103
- <textarea class="ps-textarea${error ? ' ps-is-error' : ''}" name="${name}">${value}</textarea>
104
- `
105
- }
106
- if(type == 'markdown'){
107
- return renderHtml`
108
- <textarea class="ps-textarea${error ? ' ps-is-error' : ''}" name="${name}" data-node-wrapper="anchor" data-target="_overlay" data-href="&_part=markdown-editor">${value}</textarea>
109
- `
110
- }
111
- if(type == 'checkbox'){
112
- return renderHtml`
113
- <input class="ps-input${error ? ' ps-is-error' : ''}" type="checkbox" name="${name}" type="${type}" ${value ? 'checked' : ''} />
114
- `
115
- }
116
- return renderHtml`
117
- <input class="ps-input${error ? ' ps-is-error' : ''}" name="${name}" type="${type}" value="${value}">
118
- `
119
- }}
120
- ${() => {
121
- if(error){
122
- return renderHtml`
123
- <p class="ps-is-error">${error}</p>
124
- `
125
- }
126
- }}
127
- </div>
128
- `;
129
- })}
130
- </section>
131
- <footer>
132
- <button class="ps-button" type="submit">${submitTitle}</button>
133
- <button class="ps-button" data-node-wrapper="anchor" data-action="remove">${cancelTitle}</button>
134
- </footer>
135
- </form>
137
+ })}
138
+ </div>
139
+ <div class="ps-card-footer">
140
+ <button class="ps-button" type="submit">${submitTitle}</button>
141
+ <button class="ps-button" data-node-wrapper="anchor" data-action="remove">${cancelTitle}</button>
142
+ </div>
143
+ </div>
136
144
  </div>
137
145
  `;
138
146
  },
@@ -26,15 +26,16 @@ export default ({ overload, renderList, renderHtml, params: { _headers = {} } })
26
26
  return renderHtml`
27
27
  <div class="ps-modal" data-node-wrapper="anchor" data-action="remove" data-ignore-events-from-children="true">
28
28
  <button data-node-wrapper="anchor" data-action="remove"></button>
29
- <header>
29
+ <div class="ps-card-body">
30
+ <div class="ps-card-title">
30
31
  <p>${name}</p>
31
- </header>
32
- <div>
32
+ </div>
33
+ <div class="ps-card-body">
33
34
  ${out}
34
35
  </div>
35
- <footer>
36
+ <div class="ps-card-footer">
36
37
  <button class="button" data-action="remove">Close</button>
37
- </footer>
38
+ </div>
38
39
  </div>
39
40
  </div>
40
41
  `;
@@ -0,0 +1,32 @@
1
+
2
+ import { createTransport } from 'nodemailer';
3
+ import chalk from 'chalk';
4
+
5
+ export default async ({ config }) => {
6
+ const { mail: mailConfig = {} } = await config;
7
+ const transport = createTransport(mailConfig);
8
+ return (mailOptions = {}) => {
9
+ if(process.env.NODE_ENV == 'production') return transport.sendMail(mailOptions);
10
+ const { text, html, ...otherMailOptions } = mailOptions;
11
+ console.log('');
12
+ console.log('----------------------------------------');
13
+ console.log(chalk.green('sendMail'));
14
+ console.log('----------------------------------------');
15
+ Object.keys(otherMailOptions).forEach(name => {
16
+ console.log(`${name}: ${JSON.stringify(otherMailOptions[name])}`)
17
+ });
18
+ if(text){
19
+ console.log('text:')
20
+ console.log(text.replace(/(^|\n)/g, '$1 '));
21
+ }
22
+ if(html){
23
+ console.log('html:')
24
+ console.log(html.replace(/(^|\n)/g, '$1 '));
25
+ }
26
+ if(!Object.keys(mailOptions).length){
27
+ console.log('No mail options have been provided.')
28
+ }
29
+ console.log('----------------------------------------');
30
+ console.log('');
31
+ };
32
+ };
@@ -0,0 +1,6 @@
1
+
2
+ import { ServiceFactory } from '../service_factory.js';
3
+
4
+ export default () => {
5
+ return Object.keys(ServiceFactory.classes).sort();
6
+ };
@@ -0,0 +1,10 @@
1
+
2
+ export default ({ viewNames }) => {
3
+ const nonComponentStylesheetViewNames = [
4
+ 'stylesheets/vars.css',
5
+ 'stylesheets/reset.css',
6
+ 'stylesheets/global.css',
7
+ ];
8
+ const componentStylesheetViewNames = viewNames.filter(viewName => viewName.match(/\.css$/) && !nonComponentStylesheetViewNames.includes(viewName))
9
+ return [ ...nonComponentStylesheetViewNames, ...componentStylesheetViewNames ];
10
+ };
@@ -0,0 +1,6 @@
1
+
2
+ import { View } from '../view.js';
3
+
4
+ export default () => {
5
+ return Object.keys(View.classes).sort();
6
+ };
@@ -1,10 +1,12 @@
1
1
 
2
- export default async ({ params: { title, body, isSignedIn, user }, renderHtml }) => renderHtml`
2
+ export default async ({ params: { title, body, isSignedIn, user }, renderHtml, stylesheetViewNames }) => renderHtml`
3
3
  <!DOCTYPE html>
4
4
  <html>
5
5
  <head>
6
6
  <title>${title}</title>
7
- <link rel="stylesheet" href="/base.css">
7
+ ${stylesheetViewNames.map(viewName => renderHtml`
8
+ <link rel="stylesheet" href="/${viewName}">
9
+ `)}
8
10
  <script src="/bundle.js"></script>
9
11
  </head>
10
12
  <body>
@@ -1,10 +1,5 @@
1
1
 
2
2
  import * as crypto from 'crypto';
3
- import * as uuid from 'uuid';
4
-
5
- if(!crypto.randomUUID){
6
- crypto.randomUUID = uuid.v4;
7
- }
8
3
 
9
4
  export default async ({ renderForm, database, renderHtml }) => renderForm({
10
5
  title: 'Sign In',