pinstripe 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/cli.js +46 -0
- package/lib/class.js +64 -0
- package/lib/client.js +20 -0
- package/lib/command.js +39 -0
- package/lib/commands/_file_importer.js +1 -0
- package/lib/commands/drop_database.js +6 -0
- package/lib/commands/generate_command.js +39 -0
- package/lib/commands/generate_component.js +37 -0
- package/lib/commands/generate_migration.js +55 -0
- package/lib/commands/generate_model.js +43 -0
- package/lib/commands/generate_project.js +133 -0
- package/lib/commands/generate_service.js +32 -0
- package/lib/commands/generate_static_site.js +74 -0
- package/lib/commands/generate_view.js +44 -0
- package/lib/commands/init_database.js +9 -0
- package/lib/commands/list_commands.js +15 -0
- package/lib/commands/list_components.js +16 -0
- package/lib/commands/list_migrations.js +15 -0
- package/lib/commands/list_models.js +15 -0
- package/lib/commands/list_services.js +15 -0
- package/lib/commands/list_views.js +16 -0
- package/lib/commands/migrate_database.js +6 -0
- package/lib/commands/purge_old_sessions.js +12 -0
- package/lib/commands/reset_database.js +9 -0
- package/lib/commands/seed_database.js +6 -0
- package/lib/commands/show_config.js +6 -0
- package/lib/commands/start_repl.js +6 -0
- package/lib/commands/start_server.js +9 -0
- package/lib/component.js +56 -24
- package/lib/component_event.js +28 -0
- package/lib/components/_file_importer.js +1 -0
- package/lib/components/{anchor.js → a.js} +2 -3
- package/lib/components/body.js +2 -4
- package/lib/components/document.js +2 -4
- package/lib/components/form.js +2 -4
- package/lib/components/{frame.js → pinstripe_frame.js} +2 -4
- package/lib/components/{markdown_editor.js → pinstripe_markdown_editor.js} +4 -4
- package/lib/components/{overlay.js → pinstripe_overlay.js} +2 -4
- package/lib/components/{progress_bar.js → pinstripe_progress_bar.js} +2 -4
- package/lib/components/pinstripe_silo.js +2 -0
- package/lib/constants.js +26 -0
- package/lib/context.js +40 -0
- package/lib/database/client.js +211 -0
- package/lib/database/column_reference.js +13 -0
- package/lib/database/constants.js +87 -0
- package/lib/database/index.js +7 -0
- package/lib/database/migration.js +30 -0
- package/lib/database/migrator.js +28 -0
- package/lib/database/row.js +392 -0
- package/lib/database/singleton.js +12 -0
- package/lib/database/table.js +516 -0
- package/lib/database/table_reference.js +33 -0
- package/lib/database/union.js +128 -0
- package/lib/database.js +139 -0
- package/lib/defer.js +35 -0
- package/lib/defer.test.js +37 -0
- package/lib/escape_html.js +2 -0
- package/lib/extensions/_file_importer.js +2 -0
- package/lib/extensions/multi-app/index.js +4 -0
- package/lib/extensions/multi-app/views/_file_importer.js +2 -0
- package/lib/extensions/multi-app/views/apps/default.js +6 -0
- package/lib/extensions/multi-app/views/apps/guard.js +7 -0
- package/lib/extensions/multi-app/views/guard.js +21 -0
- package/lib/extensions/multi-tenant/database/row.js +27 -0
- package/lib/extensions/multi-tenant/database/table.js +30 -0
- package/lib/extensions/multi-tenant/database.js +8 -0
- package/lib/extensions/multi-tenant/index.js +4 -0
- package/lib/extensions/multi-tenant/migrations/1627976174_create_tenant_table_and_add_tenant_id_to_existing_tables.js +20 -0
- package/lib/extensions/multi-tenant/migrations/_file_importer.js +2 -0
- package/lib/extensions/multi-tenant/services/_file_importer.js +2 -0
- package/lib/extensions/multi-tenant/services/database.js +32 -0
- package/lib/html.js +72 -0
- package/lib/import_all.js +94 -0
- package/lib/index.js +10 -2
- package/lib/inflector.js +173 -0
- package/lib/model.js +110 -0
- package/lib/project.js +71 -0
- package/lib/registry.js +133 -0
- package/lib/service_consumer.js +16 -0
- package/lib/service_factory.js +20 -0
- package/lib/services/_file_importer.js +1 -0
- package/lib/services/args.js +9 -0
- package/lib/services/bot.js +69 -0
- package/lib/services/cli_utils.js +77 -0
- package/lib/services/client_builder.js +68 -0
- package/lib/services/config.js +63 -0
- package/lib/services/cookies.js +19 -0
- package/lib/services/create_model.js +8 -0
- package/lib/services/database.js +14 -0
- package/lib/services/defer.js +8 -0
- package/lib/services/fetch.js +116 -0
- package/lib/services/format_date.js +8 -0
- package/lib/services/fs_builder.js +132 -0
- package/lib/services/inflector.js +8 -0
- package/lib/services/initial_params.js +13 -0
- package/lib/services/params.js +13 -0
- package/lib/services/parse_html.js +8 -0
- package/lib/services/project.js +8 -0
- package/lib/services/render_form.js +161 -0
- package/lib/services/render_html.js +8 -0
- package/lib/services/render_markdown.js +43 -0
- package/lib/services/render_view.js +8 -0
- package/lib/services/repl.js +54 -0
- package/lib/services/run_command.js +8 -0
- package/lib/services/run_in_new_workspace.js +11 -0
- package/lib/services/send_mail.js +46 -0
- package/lib/services/server.js +90 -0
- package/lib/services/view_names.js +8 -0
- package/lib/singleton.js +13 -0
- package/lib/string_reader.js +22 -0
- package/lib/trapify.js +32 -0
- package/lib/unescape_html.js +2 -0
- package/lib/unescape_html.test.js +9 -0
- package/lib/util.js +3 -0
- package/lib/validation_error.js +7 -0
- package/lib/view.js +46 -0
- package/lib/view_file_importers/ejs.js +82 -0
- package/lib/view_file_importers/md.js +42 -0
- package/lib/views/_file_importer.js +1 -0
- package/lib/views/assets/javascripts/all.js.js +7 -0
- package/lib/views/assets/javascripts/all.js.map.js +7 -0
- package/lib/views/assets/stylesheets/all.css.js +28 -0
- package/lib/views/assets/stylesheets/components/button.css +43 -0
- package/lib/views/assets/stylesheets/components/card.css +29 -0
- package/lib/views/assets/stylesheets/components/form.css +4 -0
- package/lib/views/assets/stylesheets/components/frame.css +3 -0
- package/lib/views/assets/stylesheets/components/input.css +40 -0
- package/lib/views/assets/stylesheets/components/label.css +9 -0
- package/lib/views/assets/stylesheets/components/modal.css +62 -0
- package/lib/views/assets/stylesheets/components/overlay.css +11 -0
- package/lib/views/assets/stylesheets/components/pagination.css +60 -0
- package/lib/views/assets/stylesheets/components/progress_bar.css +20 -0
- package/lib/views/assets/stylesheets/components/textarea.css +10 -0
- package/lib/views/assets/stylesheets/global.css +120 -0
- package/lib/views/assets/stylesheets/reset.css +74 -0
- package/lib/views/assets/stylesheets/vars.css +25 -0
- package/lib/virtual_node.js +171 -0
- package/lib/virtual_node.test.js +28 -0
- package/lib/workspace.js +21 -0
- package/package.json +28 -5
- package/lib/components/index.js +0 -9
- package/lib/event_wrapper.js +0 -26
package/README.md
ADDED
package/cli.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawn } from 'child_process';
|
|
4
|
+
|
|
5
|
+
import { Project } from './lib/project.js';
|
|
6
|
+
import { Command } from './lib/command.js';
|
|
7
|
+
import { importAll } from './lib/import_all.js';
|
|
8
|
+
import { Workspace } from './lib/workspace.js';
|
|
9
|
+
|
|
10
|
+
(async () => {
|
|
11
|
+
const { entryPath, localPinstripePath, exists } = await Project.instance;
|
|
12
|
+
const { argv, env, execPath } = process;
|
|
13
|
+
const args = argv.slice(2);
|
|
14
|
+
|
|
15
|
+
if (env.IS_LOCAL_PINSTRIPE != 'true' && localPinstripePath) {
|
|
16
|
+
spawn(execPath, [localPinstripePath, ...args], {
|
|
17
|
+
env: { ...env, IS_LOCAL_PINSTRIPE: 'true' },
|
|
18
|
+
stdio: 'inherit'
|
|
19
|
+
});
|
|
20
|
+
} else {
|
|
21
|
+
if(entryPath){
|
|
22
|
+
import(entryPath);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
await importAll();
|
|
26
|
+
|
|
27
|
+
if(exists){
|
|
28
|
+
Command.unregister('generate-project');
|
|
29
|
+
} else {
|
|
30
|
+
const allowedCommands = ['generate-project', 'list-commands'];
|
|
31
|
+
Command.names.forEach(commandName => {
|
|
32
|
+
if(!allowedCommands.includes(commandName)){
|
|
33
|
+
Command.unregister(commandName);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
await Workspace.run(async function(){
|
|
40
|
+
await this.runCommand(...args);
|
|
41
|
+
});
|
|
42
|
+
} catch(e) {
|
|
43
|
+
console.error(e);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
})();
|
package/lib/class.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
|
|
2
|
+
export class Class {
|
|
3
|
+
|
|
4
|
+
static extend(){
|
|
5
|
+
return class extends this {};
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
static include(...includes){
|
|
9
|
+
includes.forEach(include => {
|
|
10
|
+
if(typeof include.meta == 'function') include.meta.call(this);
|
|
11
|
+
this.prototype.assignProps(include, name => name != 'meta');
|
|
12
|
+
});
|
|
13
|
+
return this;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static assignProps(...sources){
|
|
17
|
+
return assignProps(this, ...sources);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static new(...args){
|
|
21
|
+
return new this(...args);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static get parent(){
|
|
25
|
+
return this.__proto__;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
constructor(...args){
|
|
29
|
+
let out = this.initialize(...args);
|
|
30
|
+
if(typeof out?.then == 'function'){
|
|
31
|
+
return out.then(out => out || this);
|
|
32
|
+
}
|
|
33
|
+
return out || this;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
initialize(){
|
|
37
|
+
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
assignProps(...sources){
|
|
41
|
+
return assignProps(this, ...sources);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const assignProps = (target, ...sources) => {
|
|
46
|
+
const fn = typeof sources[sources.length - 1] == 'function' ? sources.pop() : () => true;
|
|
47
|
+
|
|
48
|
+
sources.forEach(source => {
|
|
49
|
+
Object.getOwnPropertyNames(source).forEach(name => {
|
|
50
|
+
if(!fn(name)){
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const descriptor = { ...Object.getOwnPropertyDescriptor(source, name) };
|
|
54
|
+
const { get: targetGet, set: targetSet } = (Object.getOwnPropertyDescriptor(target, name) || {});
|
|
55
|
+
const { get = targetGet, set = targetSet } = descriptor;
|
|
56
|
+
|
|
57
|
+
if(get) descriptor.get = get;
|
|
58
|
+
if(set) descriptor.set = set;
|
|
59
|
+
|
|
60
|
+
Object.defineProperty(target, name, descriptor);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
return target;
|
|
64
|
+
};
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { fileURLToPath } from 'url';
|
|
2
|
+
|
|
3
|
+
import { Class } from './class.js';
|
|
4
|
+
import { Singleton } from './singleton.js';
|
|
5
|
+
|
|
6
|
+
export const Client = Class.extend().include({
|
|
7
|
+
meta(){
|
|
8
|
+
this.include(Singleton);
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
initialize(){
|
|
12
|
+
this.modules = [];
|
|
13
|
+
|
|
14
|
+
this.addModule(`import ${JSON.stringify(fileURLToPath(`${import.meta.url}/../index.js`))};`);
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
addModule(...modules){
|
|
18
|
+
this.modules.push(...modules);
|
|
19
|
+
}
|
|
20
|
+
});
|
package/lib/command.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
|
|
2
|
+
import { Class } from './class.js';
|
|
3
|
+
import { inflector } from './inflector.js';
|
|
4
|
+
import { Registry } from './registry.js';
|
|
5
|
+
import { ServiceConsumer } from './service_consumer.js';
|
|
6
|
+
|
|
7
|
+
export const Command = Class.extend().include({
|
|
8
|
+
meta(){
|
|
9
|
+
this.include(Registry);
|
|
10
|
+
this.include(ServiceConsumer);
|
|
11
|
+
|
|
12
|
+
this.assignProps({
|
|
13
|
+
normalizeName: (...args) => inflector.dasherize(...args),
|
|
14
|
+
|
|
15
|
+
get schedules(){
|
|
16
|
+
if(!this.hasOwnProperty('_schedules')){
|
|
17
|
+
this._schedules = [];
|
|
18
|
+
}
|
|
19
|
+
return this._schedules;
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
schedule(...args){
|
|
23
|
+
this.schedules.push(args);
|
|
24
|
+
return this;
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
async run(context, name = 'list-commands', ...args){
|
|
28
|
+
return context.fork().run(async context => {
|
|
29
|
+
context.args = [ ...args ];
|
|
30
|
+
await this.create(name, context).run();
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
run(){
|
|
37
|
+
console.error(`No such command "${this.constructor.name}" exists.`);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Command as default } from 'pinstripe';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
async run(){
|
|
5
|
+
const [ name = '' ] = this.args;
|
|
6
|
+
const normalizedName = this.inflector.snakeify(name);
|
|
7
|
+
if(normalizedName == ''){
|
|
8
|
+
console.error('A command name must be given.');
|
|
9
|
+
process.exit();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { inProjectRootDir, generateFile, line, indent } = this.fsBuilder;
|
|
13
|
+
|
|
14
|
+
await inProjectRootDir(async () => {
|
|
15
|
+
|
|
16
|
+
await generateFile(`lib/commands/_file_importer.js`, { skipIfExists: true }, () => {
|
|
17
|
+
line();
|
|
18
|
+
line(`export { Command as default } from 'pinstripe';`);
|
|
19
|
+
line();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
await generateFile(`lib/commands/${normalizedName}.js`, () => {
|
|
23
|
+
line();
|
|
24
|
+
line(`export default {`);
|
|
25
|
+
indent(() => {
|
|
26
|
+
line('run(){');
|
|
27
|
+
indent(() => {
|
|
28
|
+
line(`console.log('${this.inflector.dasherize(normalizedName)} command coming soon!')`);
|
|
29
|
+
});
|
|
30
|
+
line('}');
|
|
31
|
+
});
|
|
32
|
+
line('};');
|
|
33
|
+
line();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
|
|
2
|
+
export default {
|
|
3
|
+
async run(){
|
|
4
|
+
const [ name = '' ] = this.args;
|
|
5
|
+
const normalizedName = this.inflector.snakeify(name);
|
|
6
|
+
if(normalizedName == ''){
|
|
7
|
+
console.error('A node wrapper name must be given.');
|
|
8
|
+
process.exit();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { inProjectRootDir, generateFile, line, indent } = this.fsBuilder;
|
|
12
|
+
|
|
13
|
+
await inProjectRootDir(async () => {
|
|
14
|
+
|
|
15
|
+
await generateFile(`lib/components/_file_importer.js`, { skipIfExists: true }, () => {
|
|
16
|
+
line();
|
|
17
|
+
line(`export { Component as default } from 'pinstripe';`);
|
|
18
|
+
line();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
await generateFile(`lib/components/${normalizedName}.js`, () => {
|
|
22
|
+
line();
|
|
23
|
+
line(`export default {`);
|
|
24
|
+
indent(() => {
|
|
25
|
+
line(`initialize(){`);
|
|
26
|
+
indent(() => {
|
|
27
|
+
line(`this.constructor.parent.prototype.initialize.call(this, ...args);`);
|
|
28
|
+
});
|
|
29
|
+
line(`}`);
|
|
30
|
+
});
|
|
31
|
+
line('};');
|
|
32
|
+
line();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
|
|
2
|
+
export default {
|
|
3
|
+
async run(){
|
|
4
|
+
const { extractArg, extractFields, extractOptions } = this.cliUtils;
|
|
5
|
+
|
|
6
|
+
const suffix = this.inflector.snakeify(extractArg('migration'));
|
|
7
|
+
const fields = extractFields();
|
|
8
|
+
const { table } = extractOptions({
|
|
9
|
+
table: (() => {
|
|
10
|
+
const matches = suffix.match(/_to_(.+)$/);
|
|
11
|
+
if(matches){
|
|
12
|
+
return matches[1];
|
|
13
|
+
}
|
|
14
|
+
})()
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const unixTime = Math.floor(new Date().getTime() / 1000);
|
|
18
|
+
const name = `${unixTime}_${suffix}`;
|
|
19
|
+
|
|
20
|
+
const { inProjectRootDir, generateFile, line, indent } = this.fsBuilder;
|
|
21
|
+
|
|
22
|
+
await inProjectRootDir(async () => {
|
|
23
|
+
|
|
24
|
+
await generateFile(`lib/migrations/_file_importer.js`, { skipIfExists: true }, () => {
|
|
25
|
+
line();
|
|
26
|
+
line(`export { Migration as default } from 'pinstripe/database';`);
|
|
27
|
+
line();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
await generateFile(`lib/migrations/${name}.js`, () => {
|
|
31
|
+
line();
|
|
32
|
+
line(`export default {`);
|
|
33
|
+
indent(() => {
|
|
34
|
+
line(`async migrate(){`);
|
|
35
|
+
indent(() => {
|
|
36
|
+
if(table && fields.length){
|
|
37
|
+
line(`await this.database.table('${table}', async ${table} => {`);
|
|
38
|
+
indent(() => {
|
|
39
|
+
fields.forEach(({ name, type }) => {
|
|
40
|
+
line(`await ${table}.addColumn('${name}', '${type}');`);
|
|
41
|
+
});
|
|
42
|
+
})
|
|
43
|
+
line(`});`);
|
|
44
|
+
} else {
|
|
45
|
+
line();
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
line(`}`);
|
|
49
|
+
})
|
|
50
|
+
line('};');
|
|
51
|
+
line();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
|
|
2
|
+
export default {
|
|
3
|
+
async run(){
|
|
4
|
+
|
|
5
|
+
const { extractArg, extractFields } = this.cliUtils;
|
|
6
|
+
const name = this.inflector.snakeify(extractArg(''));
|
|
7
|
+
if(name == ''){
|
|
8
|
+
console.error('A model name must be given.');
|
|
9
|
+
process.exit();
|
|
10
|
+
}
|
|
11
|
+
const fields = extractFields();
|
|
12
|
+
|
|
13
|
+
const collectionName = this.inflector.camelize(this.inflector.pluralize(name));
|
|
14
|
+
if(!await this.database[collectionName]){
|
|
15
|
+
const denormalizedFields = fields.map(({ mandatory, name, type }) => {
|
|
16
|
+
return `${ mandatory ? '^' : '' }${name}:${type}`
|
|
17
|
+
});
|
|
18
|
+
await this.runCommand('generate-migration', `create_${name}`, ...denormalizedFields, '--table', collectionName)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const { inProjectRootDir, generateFile, line, indent } = this.fsBuilder;
|
|
22
|
+
|
|
23
|
+
await inProjectRootDir(async () => {
|
|
24
|
+
|
|
25
|
+
await generateFile(`lib/models/_file_importer.js`, { skipIfExists: true }, () => {
|
|
26
|
+
line();
|
|
27
|
+
line(`export { Row as default } from 'pinstripe/database';`);
|
|
28
|
+
line();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
await generateFile(`lib/models/${name}.js`, () => {
|
|
32
|
+
line();
|
|
33
|
+
line(`export default {`);
|
|
34
|
+
indent(() => {
|
|
35
|
+
line();
|
|
36
|
+
});
|
|
37
|
+
line('};');
|
|
38
|
+
line();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
|
|
2
|
+
import { spawnSync } from 'child_process';
|
|
3
|
+
import * as crypto from 'crypto';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
async run(){
|
|
7
|
+
|
|
8
|
+
const { extractArg, extractOptions } = this.cliUtils;
|
|
9
|
+
const name = extractArg('');
|
|
10
|
+
if(name == ''){
|
|
11
|
+
console.error('A project name must be given.');
|
|
12
|
+
process.exit();
|
|
13
|
+
}
|
|
14
|
+
const { with: dependencies } = extractOptions({
|
|
15
|
+
with: []
|
|
16
|
+
});
|
|
17
|
+
if(!dependencies.includes('pinstripe')){
|
|
18
|
+
dependencies.unshift('pinstripe')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const { generateDir, generateFile, line, indent, echo } = this.fsBuilder;
|
|
22
|
+
|
|
23
|
+
await generateDir(name, async () => {
|
|
24
|
+
|
|
25
|
+
await generateFile(`package.json`, () => {
|
|
26
|
+
echo(JSON.stringify({
|
|
27
|
+
type: "module",
|
|
28
|
+
name,
|
|
29
|
+
version: "0.0.0",
|
|
30
|
+
license: "MIT",
|
|
31
|
+
exports: {
|
|
32
|
+
".": "./lib/index.js"
|
|
33
|
+
},
|
|
34
|
+
}, null, 2));
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
await generateFile(`pinstripe.config.js`, () => {
|
|
38
|
+
line();
|
|
39
|
+
line(`const environment = process.env.NODE_ENV || 'development';`);
|
|
40
|
+
line();
|
|
41
|
+
line(`let database;`);
|
|
42
|
+
line(`if(environment == 'production'){`)
|
|
43
|
+
indent(() => {
|
|
44
|
+
line(`database = {`);
|
|
45
|
+
indent(() => {
|
|
46
|
+
line(`adapter: 'mysql',`)
|
|
47
|
+
line(`host: 'localhost',`);
|
|
48
|
+
line(`user: 'root',`);
|
|
49
|
+
line(`password: '',`);
|
|
50
|
+
line(`database: \`${this.inflector.snakeify(name)}_\${environment}\``);
|
|
51
|
+
});
|
|
52
|
+
line(`};`);
|
|
53
|
+
});
|
|
54
|
+
line(`} else {`);
|
|
55
|
+
indent(() => {
|
|
56
|
+
line(`database = {`);
|
|
57
|
+
indent(() => {
|
|
58
|
+
line(`adapter: 'sqlite',`);
|
|
59
|
+
line(`filename: \`\${environment}.db\``)
|
|
60
|
+
});
|
|
61
|
+
line(`};`);
|
|
62
|
+
});
|
|
63
|
+
line(`}`);
|
|
64
|
+
line();
|
|
65
|
+
line(`let mail;`);
|
|
66
|
+
line(`if(environment == 'production'){`)
|
|
67
|
+
indent(() => {
|
|
68
|
+
line(`mail = {`);
|
|
69
|
+
indent(() => {
|
|
70
|
+
line(`adapter: 'smtp',`)
|
|
71
|
+
line(`host: "smtp.example.com",`)
|
|
72
|
+
line(`port: 465,`);
|
|
73
|
+
line(`secure: true, // use TLS`);
|
|
74
|
+
line(`auth: {`);
|
|
75
|
+
indent(() => {
|
|
76
|
+
line(`user: "username",`);
|
|
77
|
+
line(`pass: "password",`);
|
|
78
|
+
});
|
|
79
|
+
line(`}`);
|
|
80
|
+
});
|
|
81
|
+
line(`};`);
|
|
82
|
+
});
|
|
83
|
+
line(`} else {`);
|
|
84
|
+
indent(() => {
|
|
85
|
+
line(`mail = {`);
|
|
86
|
+
indent(() => {
|
|
87
|
+
line(`adapter: 'dummy'`);
|
|
88
|
+
});
|
|
89
|
+
line(`};`);
|
|
90
|
+
});
|
|
91
|
+
line(`}`);
|
|
92
|
+
line();
|
|
93
|
+
line(`export default {`);
|
|
94
|
+
indent(() => {
|
|
95
|
+
line(`database,`);
|
|
96
|
+
line(`mail,`);
|
|
97
|
+
line(`salt: '${crypto.randomUUID()}'`)
|
|
98
|
+
})
|
|
99
|
+
line(`};`);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
await generateFile(`lib/index.js`, () => {
|
|
103
|
+
line();
|
|
104
|
+
line(`import { importAll } from 'pinstripe';`);
|
|
105
|
+
dependencies.forEach((dependency) => {
|
|
106
|
+
if(dependency != 'pinstripe') line(`import '${dependency}';`);
|
|
107
|
+
});
|
|
108
|
+
line();
|
|
109
|
+
line(`importAll(import.meta.url);`);
|
|
110
|
+
line();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
await generateFile(`README.md`, () => {
|
|
114
|
+
line();
|
|
115
|
+
line(`# ${name}`);
|
|
116
|
+
line();
|
|
117
|
+
line('## Getting started');
|
|
118
|
+
line();
|
|
119
|
+
line('```bash');
|
|
120
|
+
indent(() => {
|
|
121
|
+
line('pinstripe init-database');
|
|
122
|
+
line('pinstripe start-server');
|
|
123
|
+
});
|
|
124
|
+
line('```');
|
|
125
|
+
line();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
spawnSync('yarn', [ 'add', ...dependencies ], {
|
|
129
|
+
stdio: 'inherit'
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
export default {
|
|
3
|
+
async run(){
|
|
4
|
+
const [ name = '' ] = this.args;
|
|
5
|
+
if(name == ''){
|
|
6
|
+
console.error('A service name must be given.');
|
|
7
|
+
process.exit();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { inProjectRootDir, generateFile, line, indent } = this.fsBuilder;
|
|
11
|
+
|
|
12
|
+
await inProjectRootDir(async () => {
|
|
13
|
+
|
|
14
|
+
await generateFile(`lib/services/_file_importer.js`, { skipIfExists: true }, () => {
|
|
15
|
+
line();
|
|
16
|
+
line(`export { ServiceFactory as default } from 'pinstripe';`);
|
|
17
|
+
line();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
await generateFile(`lib/services/${this.inflector.snakeify(name)}.js`, () => {
|
|
21
|
+
line();
|
|
22
|
+
line(`export default () => {`);
|
|
23
|
+
indent(() => {
|
|
24
|
+
line(`return 'Example ${this.inflector.camelize(name)} service'`);
|
|
25
|
+
});
|
|
26
|
+
line('};');
|
|
27
|
+
line();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
|
|
2
|
+
import { default as mimeTypes } from 'mime-types';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
async run(){
|
|
6
|
+
this.pages = {};
|
|
7
|
+
const urls = this.viewNames.filter(path => !path.match(/(^|\/)_/)).map(path => {
|
|
8
|
+
return new URL(path, 'http://127.0.0.1/');
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
while(urls.length){
|
|
12
|
+
await this.crawlPage({ _url: urls.shift() });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const pages = Object.values(this.pages).filter(page => page.status == 200 && Object.keys(page.params).length == 1);
|
|
16
|
+
const { inProjectRootDir, generateDir, generateFile, echo } = this.fsBuilder;
|
|
17
|
+
|
|
18
|
+
await inProjectRootDir(async () => {
|
|
19
|
+
await generateDir('build/static', async () => {
|
|
20
|
+
while(pages.length){
|
|
21
|
+
const { params, headers } = pages.shift();
|
|
22
|
+
const contentType = headers['content-type'];
|
|
23
|
+
|
|
24
|
+
const path = params._url.pathname;
|
|
25
|
+
let filePath = path.replace(/^\//, '');
|
|
26
|
+
if(filePath.match(/(^|\/)$/)){
|
|
27
|
+
filePath = `${filePath}index`;
|
|
28
|
+
}
|
|
29
|
+
if(!filePath.match(/[^/]+\.[^/]+$/)){
|
|
30
|
+
filePath = `${filePath}.${mimeTypes.extension(contentType)}`
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const data = (await this.fetch({ _url: new URL(path, 'http://127.0.0.1/') }))[2];
|
|
34
|
+
|
|
35
|
+
await generateFile(filePath, () => {
|
|
36
|
+
echo(data.join(''));
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
async crawlPage(params){
|
|
44
|
+
const hash = JSON.stringify(params);
|
|
45
|
+
if(this.pages[hash]) return;
|
|
46
|
+
const page = { params };
|
|
47
|
+
this.pages[hash] = page;
|
|
48
|
+
const [ status, headers, data ] = await this.fetch(params);
|
|
49
|
+
page.status = status;
|
|
50
|
+
page.headers = headers;
|
|
51
|
+
if(status != 200 || headers['content-type'] != 'text/html') return;
|
|
52
|
+
const html = data.join('');
|
|
53
|
+
const virtualDom = this.parseHtml(html);
|
|
54
|
+
const urls = this.extractUrls(virtualDom);
|
|
55
|
+
while(urls.length){
|
|
56
|
+
const url = urls.shift();
|
|
57
|
+
await this.crawlPage({ ...url.params, _url: url });
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
extractUrls(virtualDom){
|
|
62
|
+
const out = [];
|
|
63
|
+
virtualDom.traverse(({ attributes }) => {
|
|
64
|
+
['src', 'href'].forEach(name => {
|
|
65
|
+
const value = attributes[name];
|
|
66
|
+
if(!value) return;
|
|
67
|
+
const url = new URL(value, 'http://127.0.0.1/');
|
|
68
|
+
if(url.host != '127.0.0.1') return;
|
|
69
|
+
out.push(url);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
|
|
2
|
+
export default {
|
|
3
|
+
async run(){
|
|
4
|
+
const [ name = '' ] = this.args;
|
|
5
|
+
let normalizedName = name.replace(/^\//, '');
|
|
6
|
+
if(name == ''){
|
|
7
|
+
console.error('A view name must be given.');
|
|
8
|
+
process.exit();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if(!normalizedName.match(/\.[^\/]+$/)){
|
|
12
|
+
normalizedName = `${normalizedName}.js`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const { inProjectRootDir, generateFile, line, indent } = this.fsBuilder;
|
|
16
|
+
|
|
17
|
+
await inProjectRootDir(async () => {
|
|
18
|
+
|
|
19
|
+
await generateFile(`lib/views/_file_importer.js`, { skipIfExists: true }, () => {
|
|
20
|
+
line();
|
|
21
|
+
line(`export { View as default } from 'pinstripe';`);
|
|
22
|
+
line();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
await generateFile(`lib/views/${normalizedName}`, () => {
|
|
26
|
+
line();
|
|
27
|
+
line('export default {');
|
|
28
|
+
indent(() => {
|
|
29
|
+
line('render(){')
|
|
30
|
+
indent(() => {
|
|
31
|
+
line('return this.renderHtml`')
|
|
32
|
+
indent(() => {
|
|
33
|
+
line(`<h1>${normalizedName} view<h1></h1>`);
|
|
34
|
+
});
|
|
35
|
+
line('`;')
|
|
36
|
+
});
|
|
37
|
+
line('}')
|
|
38
|
+
});
|
|
39
|
+
line('};');
|
|
40
|
+
line();
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { Command } from 'pinstripe';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
run(){
|
|
7
|
+
console.log('');
|
|
8
|
+
console.log('The following commands are available:');
|
|
9
|
+
console.log('');
|
|
10
|
+
Command.names.forEach(commandName => {
|
|
11
|
+
console.log(` * ${chalk.green(commandName)}`);
|
|
12
|
+
});
|
|
13
|
+
console.log('');
|
|
14
|
+
}
|
|
15
|
+
};
|