pinstripe 0.34.0 → 0.36.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 (133) hide show
  1. package/README.md +1 -1
  2. package/cli.js +53 -0
  3. package/lib/annotatable.js +1 -0
  4. package/lib/bundle.js +95 -0
  5. package/lib/class.js +1 -1
  6. package/lib/command.js +13 -0
  7. package/lib/command.test.js +70 -0
  8. package/lib/commands/_file_importer.js +1 -0
  9. package/lib/commands/generate_command.js +42 -0
  10. package/lib/commands/generate_job.js +50 -0
  11. package/lib/commands/generate_project.js +178 -0
  12. package/lib/commands/generate_service.js +38 -0
  13. package/lib/commands/generate_view.js +83 -0
  14. package/lib/commands/initialize_project.js +6 -0
  15. package/lib/commands/list_jobs.js +21 -0
  16. package/lib/commands/list_services.js +21 -0
  17. package/lib/commands/list_views.js +22 -0
  18. package/lib/commands/run_job.js +15 -0
  19. package/lib/commands/show_config.js +12 -0
  20. package/lib/commands/show_theme.js +11 -0
  21. package/lib/commands/start_repl.js +12 -0
  22. package/lib/commands/start_server.js +57 -0
  23. package/lib/component.js +1 -745
  24. package/lib/constants.js +1 -1
  25. package/lib/context.js +2 -0
  26. package/lib/defer.js +2 -0
  27. package/lib/html.js +72 -0
  28. package/lib/import_all.js +9 -0
  29. package/lib/importable_registry.js +2 -0
  30. package/lib/index.js +11 -0
  31. package/lib/inflector.js +1 -1
  32. package/lib/initialize.js +24 -7
  33. package/lib/job.js +43 -0
  34. package/lib/json.js +47 -0
  35. package/lib/json.test.js +399 -0
  36. package/lib/lru_cache.js +1 -1
  37. package/lib/missing_resource_error.js +2 -0
  38. package/lib/model.js +72 -0
  39. package/lib/project.js +70 -0
  40. package/lib/registry.js +1 -1
  41. package/lib/service_factory.js +20 -0
  42. package/lib/service_file_importers/js.js +49 -0
  43. package/lib/services/_file_importer.js +1 -0
  44. package/lib/services/bundler.js +11 -0
  45. package/lib/services/call_handler.js +94 -0
  46. package/lib/services/config.js +84 -0
  47. package/lib/services/cookies.js +18 -0
  48. package/lib/services/create_model.js +8 -0
  49. package/lib/services/css_classes_for.js +13 -0
  50. package/lib/services/defer.js +12 -0
  51. package/lib/services/environment.js +24 -0
  52. package/lib/services/feature_flags.js +32 -0
  53. package/lib/services/format_date.js +8 -0
  54. package/lib/services/fs_builder.js +94 -0
  55. package/lib/services/inflector.js +8 -0
  56. package/lib/services/initial_params.js +14 -0
  57. package/lib/services/is_client.js +10 -0
  58. package/lib/services/job_coordinator.js +20 -0
  59. package/lib/services/job_queue.js +20 -0
  60. package/lib/services/job_scheduler.js +70 -0
  61. package/lib/services/job_worker.js +47 -0
  62. package/lib/services/params.js +14 -0
  63. package/lib/services/parse_html.js +8 -0
  64. package/lib/services/project.js +8 -0
  65. package/lib/services/render_form.js +147 -0
  66. package/lib/services/render_html.js +12 -0
  67. package/lib/services/render_json.js +12 -0
  68. package/lib/services/render_redirect.js +5 -0
  69. package/lib/services/render_table.js +48 -0
  70. package/lib/services/render_tag.js +26 -0
  71. package/lib/services/render_text.js +8 -0
  72. package/lib/services/render_view.js +16 -0
  73. package/lib/services/repl.js +54 -0
  74. package/lib/services/run_command.js +8 -0
  75. package/lib/services/run_in_new_workspace.js +18 -0
  76. package/lib/services/run_job.js +9 -0
  77. package/lib/services/send_mail.js +64 -0
  78. package/lib/services/server.js +227 -0
  79. package/lib/services/service_manager.js +12 -0
  80. package/lib/services/service_worker.js +99 -0
  81. package/lib/services/theme.js +11 -0
  82. package/lib/services/trapify.js +8 -0
  83. package/lib/services/version.js +27 -0
  84. package/lib/services/view.js +6 -0
  85. package/lib/services/view_map.js +83 -0
  86. package/lib/services/view_map.test.js +36 -0
  87. package/lib/singleton.js +2 -0
  88. package/lib/text.js +10 -0
  89. package/lib/theme.js +75 -0
  90. package/lib/theme.test.js +30 -0
  91. package/lib/theme_default_design_tokens.js +455 -0
  92. package/lib/trapify.js +1 -1
  93. package/lib/validateable.js +2 -0
  94. package/lib/validation_error.js +2 -0
  95. package/lib/view.js +132 -0
  96. package/lib/view_file_importers/js.js +62 -0
  97. package/lib/views/_file_importer.js +1 -0
  98. package/lib/views/_pinstripe/_button.js +154 -0
  99. package/lib/views/_pinstripe/_content.js +256 -0
  100. package/lib/views/_pinstripe/_form.js +301 -0
  101. package/lib/views/_pinstripe/_pagination.js +47 -0
  102. package/lib/views/_pinstripe/_panel.js +69 -0
  103. package/lib/views/_pinstripe/_shell/environment.json.js +8 -0
  104. package/lib/views/_pinstripe/_shell/feature_flags.json.js +8 -0
  105. package/lib/views/_pinstripe/_shell/index.js +206 -0
  106. package/lib/views/_pinstripe/_shell/service_worker.js.map.js +7 -0
  107. package/lib/views/_pinstripe/_shell/styles.css.js +61 -0
  108. package/lib/views/_pinstripe/_shell/version.json.js +8 -0
  109. package/lib/views/_pinstripe/_shell/window.js.js +7 -0
  110. package/lib/views/_pinstripe/_shell/window.js.map.js +7 -0
  111. package/lib/views/_pinstripe/_table.js +139 -0
  112. package/lib/views/_placeholders/overlay.js +55 -0
  113. package/lib/views/service_worker.js.js +7 -0
  114. package/lib/views/up.js +11 -0
  115. package/lib/virtual_node.js +1 -1
  116. package/lib/workspace.js +4 -0
  117. package/package.json +32 -8
  118. package/lib/component_event.js +0 -28
  119. package/lib/components/helpers.js +0 -74
  120. package/lib/components/index.js +0 -13
  121. package/lib/components/pinstripe_anchor.js +0 -25
  122. package/lib/components/pinstripe_body.js +0 -42
  123. package/lib/components/pinstripe_document.js +0 -92
  124. package/lib/components/pinstripe_form.js +0 -65
  125. package/lib/components/pinstripe_frame.js +0 -79
  126. package/lib/components/pinstripe_menu.js +0 -33
  127. package/lib/components/pinstripe_modal.js +0 -162
  128. package/lib/components/pinstripe_overlay.js +0 -51
  129. package/lib/components/pinstripe_popover.js +0 -86
  130. package/lib/components/pinstripe_progress_bar.js +0 -78
  131. package/lib/components/pinstripe_script.js +0 -12
  132. package/lib/components/pinstripe_skeleton.js +0 -57
  133. package/lib/proof_of_work.js +0 -2
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  # Welcome to Pinstripe
3
3
 
4
- A slick web framework for your browser.
4
+ A slick web framework for Node.js.
5
5
 
6
6
  ## License
7
7
 
package/cli.js ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+
3
+ import 'haberdash/node';
4
+ import chalk from 'chalk';
5
+
6
+ import { Project } from './lib/project.js';
7
+ import { Command } from './lib/command.js';
8
+ import { importAll } from './lib/import_all.js';
9
+ import { Workspace } from './lib/workspace.js';
10
+ import { ValidationError } from 'haberdash';
11
+ import { inflector } from '@pinstripe/utils';
12
+
13
+ (async () => {
14
+ const { entryPath, exists } = await Project.instance;
15
+ const { argv } = process;
16
+ const [name, ...args ] = argv.slice(2);
17
+
18
+ if(entryPath){
19
+ import(entryPath);
20
+ }
21
+
22
+ await importAll();
23
+
24
+ if(process.env.PINSTRIPE_KEEP_ALL_COMMANDS != 'true') {
25
+ if(exists){
26
+ Command.unregister('generate-project');
27
+ Command.unregister('initialize-project');
28
+ } else {
29
+ Command.names.forEach(commandName => {
30
+ if(commandName == 'list-commands' || commandName == 'generate-project') return;
31
+ Command.unregister(commandName);
32
+ });
33
+ }
34
+ }
35
+
36
+ try {
37
+ await Workspace.run(async function(){
38
+ await this.runCommand(name, args);
39
+ });
40
+ } catch(error) {
41
+ if(error instanceof ValidationError) {
42
+ console.error('');
43
+ console.error('There was an error validating the command parameters:');
44
+ console.error('');
45
+ for(const [field, message] of Object.entries(error.errors)){
46
+ console.error(` * ${chalk.red(`${inflector.dasherize(field)}: ${message}`)}`);
47
+ }
48
+ console.error('');
49
+ process.exit();
50
+ }
51
+ console.error(error);
52
+ }
53
+ })();
@@ -0,0 +1 @@
1
+ export * from 'haberdash/lib/annotatable.js';
package/lib/bundle.js ADDED
@@ -0,0 +1,95 @@
1
+
2
+ import { build } from 'esbuild';
3
+ import { promisify } from 'util';
4
+ import { writeFile, readFile } from 'fs';
5
+ import { dirSync } from 'tmp';
6
+ import { fileURLToPath } from 'url';
7
+
8
+ import { Class } from './class.js';
9
+ import { AbstractImportableRegistry } from 'haberdash';
10
+ import { Project } from './project.js';
11
+
12
+ export const Bundle = Class.extend().include({
13
+ meta(){
14
+ this.include(AbstractImportableRegistry);
15
+
16
+ this.assignProps({
17
+ get modules(){
18
+ if(!this.hasOwnProperty('_modules')){
19
+ this._modules = [];
20
+ }
21
+ return this._modules;
22
+ },
23
+
24
+ addModule(environment, module){
25
+ this.register(environment, {
26
+ meta(){
27
+ this.modules.push(module);
28
+ }
29
+ });
30
+ },
31
+ });
32
+ },
33
+
34
+ async build(options = {}){
35
+ const { force = false } = options;
36
+ if(!this.constructor.buildPromise || force){
37
+ this.constructor.buildPromise = this._build();
38
+ }
39
+ return this.constructor.buildPromise;
40
+ },
41
+
42
+ async _build(){
43
+ const { name: tmpDir, removeCallback: deleteTmpDir } = dirSync({ unsafeCleanup: true });
44
+ const inFile = `${tmpDir}/in.js`;
45
+ const outFile = `${tmpDir}/out.js`;
46
+
47
+ await promisify(writeFile)(inFile, this.constructor.modules.map((_, i) => `import ${JSON.stringify(`${tmpDir}/module-${i}.js`)};`).join('\n'));
48
+
49
+ for(let i = 0; i < this.constructor.modules.length; i++){
50
+ await promisify(writeFile)(`${tmpDir}/module-${i}.js`, this.constructor.modules[i]);
51
+ }
52
+
53
+ const { errors } = await build({
54
+ entryPoints: [inFile],
55
+ bundle: true,
56
+ sourcemap: true,
57
+ outfile: outFile,
58
+ plugins: [this.plugin],
59
+ nodePaths: await Project.instance.nodePaths,
60
+ minify: process.env.NODE_ENV == 'production',
61
+ logLevel: 'silent'
62
+ });
63
+
64
+ errors.forEach(error => console.error(error));
65
+
66
+ const out = {
67
+ js: (await promisify(readFile)(outFile, 'utf8')).replace(/\/\/#.*?$/m, ''),
68
+ map: await promisify(readFile)(`${outFile}.map`, 'utf8'),
69
+ };
70
+
71
+ await deleteTmpDir();
72
+
73
+ return out;
74
+ },
75
+
76
+ get plugin(){
77
+ return {
78
+ name: 'pinstripe',
79
+
80
+ async setup(build){
81
+ build.onLoad({ filter: /\.js$/ }, async args => {
82
+ const { path } = args;
83
+ const contents = await promisify(readFile)(path, 'utf8');
84
+ if(!contents.match(/pinstripe-if-client/)) return;
85
+ const alteredContents = contents.replace(/(.*)\/\/\s*pinstripe-if-client:\s*(.*)/g, '$2 // pinstripe-if-server: $1');
86
+ return { contents: alteredContents, loader: 'js' };
87
+ });
88
+ }
89
+ };
90
+ }
91
+ });
92
+
93
+ ['window', 'serviceWorker'].forEach(environment => {
94
+ Bundle.addModule(environment, `import ${JSON.stringify(fileURLToPath(`${import.meta.url}/../index.js`))};`);
95
+ });
package/lib/class.js CHANGED
@@ -1,2 +1,2 @@
1
1
 
2
- export { Class } from '@sintra/utils';
2
+ export { Class } from '@pinstripe/utils';
package/lib/command.js ADDED
@@ -0,0 +1,13 @@
1
+
2
+ import { Class } from './class.js';
3
+ import { AbstractCommand } from 'haberdash';
4
+ import { ServiceFactory } from './service_factory.js';
5
+
6
+ export const Command = Class.extend('Command').include({
7
+ meta(){
8
+ this.include(AbstractCommand);
9
+ this.include(ServiceFactory.Consumerable);
10
+ }
11
+ });
12
+
13
+ Command.binaryName = 'pinstripe';
@@ -0,0 +1,70 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert";
3
+
4
+ import { Command } from "./command.js";
5
+
6
+ test("Command validation through validate method", async () => {
7
+ const { Context } = await import("haberdash");
8
+ const { ValidationError } = await import("haberdash");
9
+
10
+ const TestCommand = Command.extend();
11
+ TestCommand.hasParam("name", { type: "string" });
12
+ TestCommand.hasParam("count", { type: "number", optional: true });
13
+
14
+ const context = Context.new();
15
+
16
+ context.params = { name: "test", count: 42 };
17
+ const command1 = TestCommand.new(context);
18
+ await assert.doesNotReject(async () => {
19
+ await command1.validate();
20
+ });
21
+
22
+ context.params = { name: "test", unknownParam: "value" };
23
+ const command2 = TestCommand.new(context);
24
+ await assert.rejects(
25
+ async () => {
26
+ await command2.validate();
27
+ },
28
+ ValidationError,
29
+ "Should throw ValidationError for unknown parameters"
30
+ );
31
+
32
+ context.params = { count: 42 };
33
+ const command3 = TestCommand.new(context);
34
+ await assert.rejects(
35
+ async () => {
36
+ await command3.validate();
37
+ },
38
+ ValidationError,
39
+ "Should throw ValidationError for missing required parameters"
40
+ );
41
+
42
+ context.params = { name: "test" };
43
+ const command4 = TestCommand.new(context);
44
+ await assert.doesNotReject(async () => {
45
+ await command4.validate();
46
+ });
47
+
48
+ context.params = { name: "test", badParam: "value" };
49
+ const command5 = TestCommand.new(context);
50
+ try {
51
+ await command5.validate();
52
+ assert.fail("Expected validation to throw");
53
+ } catch (error) {
54
+ assert(error instanceof ValidationError, "Should be ValidationError");
55
+ assert(error.errors, "Should have errors property");
56
+ assert(error.errors.badParam, "Should have badParam error");
57
+ assert(
58
+ error.errors.badParam.includes("Unknown parameter"),
59
+ `Expected error message to contain "Unknown parameter", but got: ${error.errors.badParam}`
60
+ );
61
+ }
62
+ });
63
+
64
+ test("Pinstripe Command has binaryName 'pinstripe'", () => {
65
+ assert.equal(Command.binaryName, "pinstripe");
66
+ });
67
+
68
+ test("Pinstripe Command has list-commands registered", () => {
69
+ assert(Command.names.includes("list-commands"), "list-commands should be registered");
70
+ });
@@ -0,0 +1 @@
1
+ export { Command as default } from 'pinstripe';
@@ -0,0 +1,42 @@
1
+
2
+
3
+ export default {
4
+ meta(){
5
+ this.assignProps({
6
+ description: 'Generates a new command file in the lib/commands directory.'
7
+ });
8
+
9
+ this.hasParam('name', { type: 'string', alias: 'arg1', description: 'The name of the command to create (in snake_case).' });
10
+ },
11
+
12
+ async run(){
13
+ const normalizedName = this.inflector.snakeify(this.params.name);
14
+
15
+ const { inProjectRootDir, generateFile } = this.fsBuilder;
16
+
17
+ await inProjectRootDir(async () => {
18
+
19
+ await generateFile(`lib/commands/_file_importer.js`, { skipIfExists: true }, ({ line }) => {
20
+ line();
21
+ line(`export { Command as default } from 'pinstripe';`);
22
+ line();
23
+ });
24
+
25
+ await generateFile(`lib/commands/${normalizedName}.js`, ({ line, indent }) => {
26
+ line();
27
+ line(`export default {`);
28
+ indent(({ line, indent }) => {
29
+ line('run(){');
30
+ indent(({ line }) => {
31
+ line(`console.log('${this.inflector.dasherize(normalizedName)} command coming soon!')`);
32
+ });
33
+ line('}');
34
+ });
35
+ line('};');
36
+ line();
37
+ });
38
+
39
+ });
40
+ }
41
+ }
42
+
@@ -0,0 +1,50 @@
1
+
2
+
3
+ export default {
4
+ meta(){
5
+ this.assignProps({
6
+ description: 'Generates a new job file in the lib/jobs directory.'
7
+ });
8
+
9
+ this.hasParam('name', { type: 'string', alias: 'arg1', description: 'The name of the job to create (in snake_case).' });
10
+ },
11
+
12
+ async run(){
13
+ const normalizedName = this.inflector.snakeify(this.params.name);
14
+
15
+ const { inProjectRootDir, generateFile } = this.fsBuilder;
16
+
17
+ await inProjectRootDir(async () => {
18
+
19
+ await generateFile(`lib/jobs/_file_importer.js`, { skipIfExists: true }, ({ line }) => {
20
+ line();
21
+ line(`export { Job as default } from 'pinstripe';`);
22
+ line();
23
+ });
24
+
25
+ await generateFile(`lib/jobs/${normalizedName}.js`, ({ line, indent }) => {
26
+ line();
27
+ line(`export default {`);
28
+ indent(({ line, indent }) => {
29
+ line('meta(){');
30
+ indent(({ line }) => {
31
+ line(`this.schedule('* * * * *'); // run every minute`);
32
+ });
33
+ line('},');
34
+ });
35
+ line();
36
+ indent(({ line, indent }) => {
37
+ line('run(){');
38
+ indent(({ line }) => {
39
+ line(`console.log('${this.inflector.dasherize(normalizedName)} job coming soon!')`);
40
+ });
41
+ line('}');
42
+ });
43
+ line('};');
44
+ line();
45
+ });
46
+
47
+ });
48
+ }
49
+ }
50
+
@@ -0,0 +1,178 @@
1
+
2
+ import { spawnSync } from 'child_process';
3
+ import * as crypto from 'crypto';
4
+
5
+ export default {
6
+ meta(){
7
+ this.assignProps({
8
+ description: 'Generates a new Pinstripe project with the specified dependencies and configuration.'
9
+ });
10
+
11
+ this.hasParam('name', { type: 'string', alias: 'arg1', description: 'The name of the project to create.' });
12
+ this.hasParam('with', { type: 'string', optional: true, description: 'Additional dependencies to include (space-separated).' });
13
+ },
14
+
15
+ async run(){
16
+ const name = this.params.name;
17
+ const { with: _with = '' } = this.params;
18
+
19
+ const dependencies = ['pinstripe'];
20
+ _with.split(/\s+/).map(dependency => dependency.trim()).filter(Boolean).forEach(dependency => {
21
+ if(!dependencies.includes(dependency)) dependencies.push(dependency);
22
+ });
23
+
24
+ Object.assign(this, { name, dependencies });
25
+
26
+ const { generateDir } = this.fsBuilder;
27
+
28
+ await generateDir(name, async () => {
29
+ await this.generatePackageJson();
30
+
31
+ await this.generatePinstripeConfig();
32
+
33
+ await this.generateLibIndex();
34
+
35
+ await this.generateReadme();
36
+
37
+ this.installDependencies();
38
+
39
+ await this.initializeProject();
40
+ });
41
+ },
42
+
43
+ async generatePackageJson(){
44
+ const { generateFile } = this.fsBuilder;
45
+
46
+ await generateFile(`package.json`, ({ echo }) => {
47
+ echo(JSON.stringify({
48
+ type: "module",
49
+ name: this.name,
50
+ version: "0.0.0",
51
+ license: "MIT",
52
+ exports: {
53
+ ".": "./lib/index.js"
54
+ },
55
+ }, null, 2));
56
+ });
57
+ },
58
+
59
+ async generatePinstripeConfig(){
60
+ const { generateFile } = this.fsBuilder;
61
+
62
+ await generateFile(`pinstripe.config.js`, ({ line, indent }) => {
63
+ line();
64
+ line(`const environment = process.env.NODE_ENV || 'development';`);
65
+ line();
66
+ line(`let database;`);
67
+ line(`if(environment == 'production'){`)
68
+ indent(({ line, indent }) => {
69
+ line(`database = {`);
70
+ indent(({ line }) => {
71
+ line(`adapter: 'mysql',`)
72
+ line(`host: 'localhost',`);
73
+ line(`user: 'root',`);
74
+ line(`password: '',`);
75
+ line(`database: \`${this.inflector.snakeify(this.name)}_\${environment}\``);
76
+ });
77
+ line(`};`);
78
+ });
79
+ line(`} else {`);
80
+ indent(({ line, indent }) => {
81
+ line(`database = {`);
82
+ indent(({ line }) => {
83
+ line(`adapter: 'sqlite',`);
84
+ line(`filename: \`\${environment}.db\``)
85
+ });
86
+ line(`};`);
87
+ });
88
+ line(`}`);
89
+ line();
90
+ line(`let mail;`);
91
+ line(`if(environment == 'production'){`)
92
+ indent(({ line, indent }) => {
93
+ line(`mail = {`);
94
+ indent(({ line, indent }) => {
95
+ line(`adapter: 'smtp',`)
96
+ line(`host: "smtp.example.com",`)
97
+ line(`port: 465,`);
98
+ line(`secure: true, // use TLS`);
99
+ line(`auth: {`);
100
+ indent(({ line }) => {
101
+ line(`user: "username",`);
102
+ line(`pass: "password",`);
103
+ });
104
+ line(`}`);
105
+ });
106
+ line(`};`);
107
+ });
108
+ line(`} else {`);
109
+ indent(({ line, indent }) => {
110
+ line(`mail = {`);
111
+ indent(({ line }) => {
112
+ line(`adapter: 'dummy'`);
113
+ });
114
+ line(`};`);
115
+ });
116
+ line(`}`);
117
+ line();
118
+ line(`export default {`);
119
+ indent(({ line }) => {
120
+ line(`database,`);
121
+ line(`mail,`);
122
+ line(`salt: '${crypto.randomUUID()}'`)
123
+ })
124
+ line(`};`);
125
+ });
126
+ },
127
+
128
+ async generateLibIndex(){
129
+ const { generateFile } = this.fsBuilder;
130
+
131
+ await generateFile(`lib/index.js`, ({ line }) => {
132
+ line();
133
+ this.dependencies.forEach((dependency) => {
134
+ if(dependency != 'pinstripe') line(`import '${dependency}';`);
135
+ });
136
+ line();
137
+ line(`import { importAll } from 'pinstripe';`);
138
+ line();
139
+ line(`importAll(import.meta.url);`);
140
+ line();
141
+ });
142
+ },
143
+
144
+ async generateReadme(){
145
+ const { generateFile } = this.fsBuilder;
146
+
147
+ await generateFile(`README.md`, ({ line, indent }) => {
148
+ line();
149
+ line(`# ${this.name}`);
150
+ line();
151
+ line('## Getting started');
152
+ line();
153
+ line('```bash');
154
+ indent(({ line }) => {
155
+ line('pinstripe initialize-database');
156
+ line('pinstripe start-server');
157
+ });
158
+ line('```');
159
+ line();
160
+ });
161
+ },
162
+
163
+ installDependencies(){
164
+ spawnSync('npm', [ 'install', ...this.dependencies ], {
165
+ stdio: 'inherit'
166
+ });
167
+ },
168
+
169
+ initializeProject(){
170
+ spawnSync('npx', [ 'pinstripe', 'initialize-project'], {
171
+ stdio: 'inherit',
172
+ env: {
173
+ ...process.env,
174
+ PINSTRIPE_KEEP_ALL_COMMANDS: 'true',
175
+ }
176
+ });
177
+ }
178
+ };
@@ -0,0 +1,38 @@
1
+
2
+ export default {
3
+ meta(){
4
+ this.assignProps({
5
+ description: 'Generates a new service file in the lib/services directory.'
6
+ });
7
+
8
+ this.hasParam('name', { type: 'string', alias: 'arg1', description: 'The name of the service to create (in snake_case).' });
9
+ },
10
+
11
+ async run(){
12
+ const name = this.params.name;
13
+
14
+ const { inProjectRootDir, generateFile } = this.fsBuilder;
15
+
16
+ await inProjectRootDir(async () => {
17
+
18
+ await generateFile(`lib/services/_file_importer.js`, { skipIfExists: true }, ({ line }) => {
19
+ line();
20
+ line(`export { ServiceFactory as default } from 'pinstripe';`);
21
+ line();
22
+ });
23
+
24
+ await generateFile(`lib/services/${this.inflector.snakeify(name)}.js`, ({ line, indent }) => {
25
+ line(`export default {`);
26
+ indent(({ line, indent }) => {
27
+ line('create(){');
28
+ indent(({ line }) => {
29
+ line(`return 'Example ${this.inflector.camelize(name)} service'`);
30
+ });
31
+ line('}');
32
+ });
33
+ line('};');
34
+ });
35
+
36
+ });
37
+ }
38
+ };
@@ -0,0 +1,83 @@
1
+
2
+ import { View } from 'pinstripe';
3
+ import { readFile } from 'fs/promises';
4
+
5
+ export default {
6
+ meta(){
7
+ this.assignProps({
8
+ description: 'Generates a new view file in the lib/views directory.'
9
+ });
10
+
11
+ this.hasParam('name', { type: 'string', description: 'The name of the view to create (has a .js file extension by default).' });
12
+ },
13
+
14
+ async run(){
15
+ let normalizedName = this.params.name.replace(/^\//, '');
16
+
17
+ const existingFilePaths = [ ...View.for(normalizedName).filePaths ];
18
+ const existingFilePath = existingFilePaths.pop();
19
+ const existingFileExtension = (existingFilePath ? existingFilePath.match(/^.*\.([^\/]+)$/) : [])[1];
20
+ if(!normalizedName.match(/\.[^\/]+$/)){
21
+ if(existingFileExtension) {
22
+ normalizedName = `${normalizedName}.${existingFileExtension}`;
23
+ } else {
24
+ normalizedName = `${normalizedName}.js`;
25
+ }
26
+ }
27
+
28
+ const normalizedNameExtension = normalizedName.match(/^.*\.([^\/]+)$/)[1];
29
+
30
+ const useExistingFile = normalizedNameExtension == existingFileExtension;
31
+
32
+ const existingFileData = useExistingFile ? (await readFile(existingFilePath)).toString('utf8') : '';
33
+
34
+ const { inProjectRootDir, generateFile } = this.fsBuilder;
35
+
36
+ await inProjectRootDir(async () => {
37
+
38
+ await generateFile(`lib/views/_file_importer.js`, { skipIfExists: true }, ({ line }) => {
39
+ line();
40
+ line(`export { View as default } from 'pinstripe';`);
41
+ line();
42
+ });
43
+
44
+ await generateFile(`lib/views/${normalizedName}`, ({ echo, line, indent }) => {
45
+ if(useExistingFile){
46
+ echo(existingFileData);
47
+ } else if(normalizedNameExtension == 'js') {
48
+ line();
49
+ line('export const styles = `');
50
+ indent(({ line, indent }) => {
51
+ line(".root {");
52
+ indent(({ line }) => {
53
+ line("background: yellow;");
54
+ });
55
+ line("}");
56
+ });
57
+ line('`;');
58
+ line();
59
+ line('export default {');
60
+ indent(({ line, indent }) => {
61
+ line('render(){')
62
+ indent(({ line, indent }) => {
63
+ line('return this.renderHtml`')
64
+ indent(({ line, indent }) => {
65
+ line('<div class="${this.cssClasses.root}">');
66
+ indent(({ line }) => {
67
+ line(`<h1>${normalizedName} view</h1>`);
68
+ });
69
+ line(`</div>`)
70
+ });
71
+ line('`;');
72
+ });
73
+ line('}');
74
+ });
75
+ line('};');
76
+ line();
77
+ } else {
78
+ line();
79
+ }
80
+ });
81
+ });
82
+ }
83
+ };
@@ -0,0 +1,6 @@
1
+
2
+ export default {
3
+ async run(){
4
+ await this.runHook('run');
5
+ }
6
+ };
@@ -0,0 +1,21 @@
1
+
2
+ import chalk from 'chalk';
3
+ import { Job } from 'pinstripe';
4
+
5
+ export default {
6
+ meta(){
7
+ this.assignProps({
8
+ description: 'Lists all available jobs in the current project.'
9
+ });
10
+ },
11
+
12
+ run(){
13
+ console.log('');
14
+ console.log('The following jobs are available:');
15
+ console.log('');
16
+ Job.names.forEach(jobName => {
17
+ console.log(` * ${chalk.green(jobName)}`);
18
+ });
19
+ console.log('');
20
+ }
21
+ };