pinstripe 0.31.1 → 0.33.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/cli.js +23 -33
- package/lib/background_job.js +42 -0
- package/lib/background_jobs/_file_importer.js +1 -0
- package/lib/{commands → background_jobs}/purge_used_hashes.js +1 -3
- package/lib/command.js +40 -15
- package/lib/command.test.js +32 -0
- package/lib/commands/generate_app.js +5 -3
- package/lib/commands/generate_background_job.js +47 -0
- package/lib/commands/generate_command.js +2 -2
- package/lib/commands/generate_component.js +2 -3
- package/lib/commands/generate_migration.js +13 -13
- package/lib/commands/generate_model.js +6 -9
- package/lib/commands/generate_project.js +14 -9
- package/lib/commands/generate_service.js +2 -2
- package/lib/commands/generate_static_site.js +1 -3
- package/lib/commands/generate_view.js +2 -2
- package/lib/commands/list_background_jobs.js +15 -0
- package/lib/commands/list_commands.js +6 -0
- package/lib/commands/list_views.js +1 -3
- package/lib/commands/run_background_job.js +11 -0
- package/lib/commands/start_server.js +4 -7
- package/lib/component.js +7 -4
- package/lib/components/helpers.js +18 -6
- package/lib/components/pinstripe_document.js +6 -2
- package/lib/components/pinstripe_frame.js +3 -3
- package/lib/components/pinstripe_modal.js +26 -7
- package/lib/database/client.js +65 -0
- package/lib/database/constants.js +13 -1
- package/lib/database/row.js +18 -23
- package/lib/database/table.js +107 -107
- package/lib/database.js +10 -2
- package/lib/defer.js +10 -10
- package/lib/defer.test.js +11 -0
- package/lib/extensions/multi-tenant/database/row.js +22 -0
- package/lib/extensions/multi-tenant/models/_file_importer.js +1 -0
- package/lib/extensions/multi-tenant/models/tenant.js +14 -0
- package/lib/extensions/multi-tenant/services/database.js +4 -1
- package/lib/extensions/multi-tenant/services/run_background_job.js +27 -0
- package/lib/index.js +1 -0
- package/lib/inflector.js +4 -0
- package/lib/markdown.js +13 -5
- package/lib/project.js +0 -2
- package/lib/registry.js +3 -3
- package/lib/services/bot.js +9 -9
- package/lib/services/cli_utils.js +19 -71
- package/lib/services/render_form.js +10 -5
- package/lib/services/render_table.js +48 -0
- package/lib/services/run_background_job.js +8 -0
- package/lib/services/server.js +1 -1
- package/lib/views/docs/docs/guides/introduction.md +1 -1
- package/lib/views/shared/_button.js +52 -11
- package/lib/views/shared/_danger_area.js +78 -0
- package/lib/views/shared/_editable_area.js +4 -4
- package/lib/views/shared/_form.js +53 -8
- package/lib/views/shared/_pagination.js +47 -0
- package/lib/views/shared/_panel.js +4 -0
- package/lib/views/shared/_section.js +4 -0
- package/lib/views/shared/_shell/index.js +2 -1
- package/lib/views/shared/_table.js +139 -0
- package/package.json +10 -10
- package/development.db +0 -0
- package/lib/services/args.js +0 -9
package/cli.js
CHANGED
|
@@ -1,46 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { spawn } from 'child_process';
|
|
4
|
-
|
|
5
3
|
import { Project } from './lib/project.js';
|
|
6
4
|
import { Command } from './lib/command.js';
|
|
7
5
|
import { importAll } from './lib/import_all.js';
|
|
8
6
|
import { Workspace } from './lib/workspace.js';
|
|
9
7
|
|
|
10
8
|
(async () => {
|
|
11
|
-
const { entryPath,
|
|
12
|
-
const { argv
|
|
13
|
-
const args = argv.slice(2);
|
|
9
|
+
const { entryPath, exists } = await Project.instance;
|
|
10
|
+
const { argv } = process;
|
|
11
|
+
const [name, ...args ] = argv.slice(2);
|
|
14
12
|
|
|
15
|
-
if
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
stdio: 'inherit'
|
|
19
|
-
});
|
|
20
|
-
} else {
|
|
21
|
-
if(entryPath){
|
|
22
|
-
import(entryPath);
|
|
23
|
-
}
|
|
13
|
+
if(entryPath){
|
|
14
|
+
import(entryPath);
|
|
15
|
+
}
|
|
24
16
|
|
|
25
|
-
|
|
17
|
+
await importAll();
|
|
26
18
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
await
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
console.error(e);
|
|
44
|
-
}
|
|
19
|
+
if(exists){
|
|
20
|
+
Command.names.forEach(commandName => {
|
|
21
|
+
if(!Command.for(commandName).internal) Command.unregister(commandName);
|
|
22
|
+
});
|
|
23
|
+
} else {
|
|
24
|
+
Command.names.forEach(commandName => {
|
|
25
|
+
if(!Command.for(commandName).external) Command.unregister(commandName);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
await Workspace.run(async function(){
|
|
31
|
+
await this.runCommand(name, args);
|
|
32
|
+
});
|
|
33
|
+
} catch(e) {
|
|
34
|
+
console.error(e);
|
|
45
35
|
}
|
|
46
36
|
})();
|
|
@@ -0,0 +1,42 @@
|
|
|
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 BackgroundJob = Class.extend().include({
|
|
8
|
+
meta(){
|
|
9
|
+
this.assignProps({ name: 'BackgroundJob' });
|
|
10
|
+
|
|
11
|
+
this.include(Registry);
|
|
12
|
+
this.include(ServiceConsumer);
|
|
13
|
+
|
|
14
|
+
this.assignProps({
|
|
15
|
+
normalizeName(name){
|
|
16
|
+
return inflector.dasherize(name);
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
get schedules(){
|
|
20
|
+
if(!this.hasOwnProperty('_schedules')){
|
|
21
|
+
this._schedules = [];
|
|
22
|
+
}
|
|
23
|
+
return this._schedules;
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
schedule(...args){
|
|
27
|
+
this.schedules.push(args);
|
|
28
|
+
return this;
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
async run(context, name){
|
|
32
|
+
await context.fork().run(async context => {
|
|
33
|
+
await this.create(name, context).run();
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
run(){
|
|
40
|
+
console.error(`No such background job "${this.constructor.name}" exists.`);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { BackgroundJob as default } from 'pinstripe';
|
package/lib/command.js
CHANGED
|
@@ -4,9 +4,15 @@ import { inflector } from './inflector.js';
|
|
|
4
4
|
import { Registry } from './registry.js';
|
|
5
5
|
import { ServiceConsumer } from './service_consumer.js';
|
|
6
6
|
|
|
7
|
+
const optionPattern = /^-([a-z]|-[a-z\-]+)$/;
|
|
8
|
+
|
|
7
9
|
export const Command = Class.extend().include({
|
|
8
10
|
meta(){
|
|
9
|
-
this.assignProps({
|
|
11
|
+
this.assignProps({
|
|
12
|
+
name: 'Command',
|
|
13
|
+
internal: true,
|
|
14
|
+
external: false
|
|
15
|
+
});
|
|
10
16
|
|
|
11
17
|
this.include(Registry);
|
|
12
18
|
this.include(ServiceConsumer);
|
|
@@ -16,24 +22,43 @@ export const Command = Class.extend().include({
|
|
|
16
22
|
return inflector.dasherize(name);
|
|
17
23
|
},
|
|
18
24
|
|
|
19
|
-
|
|
20
|
-
if(!this.hasOwnProperty('_schedules')){
|
|
21
|
-
this._schedules = [];
|
|
22
|
-
}
|
|
23
|
-
return this._schedules;
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
schedule(...args){
|
|
27
|
-
this.schedules.push(args);
|
|
28
|
-
return this;
|
|
29
|
-
},
|
|
30
|
-
|
|
31
|
-
async run(context, name = 'list-commands', ...args){
|
|
25
|
+
async run(context, name = 'list-commands', params = {}){
|
|
32
26
|
await context.fork().run(async context => {
|
|
33
|
-
context.
|
|
27
|
+
context.params = Array.isArray(params) ? this.extractParams(params) : params;
|
|
34
28
|
await this.create(name, context).run();
|
|
35
29
|
});
|
|
36
30
|
},
|
|
31
|
+
|
|
32
|
+
extractParams(_args = []){
|
|
33
|
+
const args = [ ..._args ];
|
|
34
|
+
const out = {};
|
|
35
|
+
let currentName;
|
|
36
|
+
while(args.length){
|
|
37
|
+
const arg = args.shift();
|
|
38
|
+
const matches = arg.match(optionPattern);
|
|
39
|
+
if(matches){
|
|
40
|
+
currentName = inflector.camelize(matches[1]);
|
|
41
|
+
if(out[currentName] === undefined){
|
|
42
|
+
out[currentName] = [];
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
if(currentName === undefined){
|
|
46
|
+
currentName = 'args';
|
|
47
|
+
out[currentName] = [];
|
|
48
|
+
}
|
|
49
|
+
out[currentName].push(arg);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
Object.keys(out).forEach(name => {
|
|
53
|
+
const value = out[name];
|
|
54
|
+
if(!value.length){
|
|
55
|
+
out[name] = true;
|
|
56
|
+
} else {
|
|
57
|
+
out[name] = value.join(' ');
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
37
62
|
});
|
|
38
63
|
},
|
|
39
64
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Command } from "./command.js";
|
|
2
|
+
|
|
3
|
+
[
|
|
4
|
+
{ args: [], expectedParams: {} },
|
|
5
|
+
{ args: ["-a"], expectedParams: { a: true } },
|
|
6
|
+
{ args: ["--apple"], expectedParams: { apple: true } },
|
|
7
|
+
{ args: ["--apple", "--pear"], expectedParams: { apple: true, pear: true } },
|
|
8
|
+
{ args: ["Hello world!"], expectedParams: { args: "Hello world!" } },
|
|
9
|
+
{ args: ["Hello", "world!"], expectedParams: { args: "Hello world!" } },
|
|
10
|
+
{
|
|
11
|
+
args: [
|
|
12
|
+
"Hello", "world!",
|
|
13
|
+
"--apple",
|
|
14
|
+
"--pear",
|
|
15
|
+
"--plum-color", "purple",
|
|
16
|
+
"--other-fruit", "peach", "orange",
|
|
17
|
+
],
|
|
18
|
+
expectedParams: {
|
|
19
|
+
args: "Hello world!",
|
|
20
|
+
apple: true,
|
|
21
|
+
pear: true,
|
|
22
|
+
plumColor: "purple",
|
|
23
|
+
otherFruit: "peach orange",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
].forEach(({ args, expectedParams }, i) => {
|
|
27
|
+
test(`Command.extractParams (${i}})`, () => {
|
|
28
|
+
expect(Command.extractParams(args)).toStrictEqual(expectedParams);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
export default {
|
|
4
4
|
async run(){
|
|
5
|
-
const
|
|
5
|
+
const { name = '' } = this.params;
|
|
6
6
|
const normalizedName = this.inflector.snakeify(name);
|
|
7
7
|
if(normalizedName == ''){
|
|
8
|
-
console.error('An app name must be given.');
|
|
8
|
+
console.error('An app --name must be given.');
|
|
9
9
|
process.exit();
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -35,7 +35,9 @@ export default {
|
|
|
35
35
|
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
await this.runCommand('generate-view',
|
|
38
|
+
await this.runCommand('generate-view', {
|
|
39
|
+
name: `${normalizedName}/index`
|
|
40
|
+
});
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
43
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
async run(){
|
|
5
|
+
const { name = '' } = this.params;
|
|
6
|
+
const normalizedName = this.inflector.snakeify(name);
|
|
7
|
+
if(normalizedName == ''){
|
|
8
|
+
console.error('A background job --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/background_jobs/_file_importer.js`, { skipIfExists: true }, () => {
|
|
17
|
+
line();
|
|
18
|
+
line(`export { BackgroundJob as default } from 'pinstripe';`);
|
|
19
|
+
line();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
await generateFile(`lib/background_jobs/${normalizedName}.js`, () => {
|
|
23
|
+
line();
|
|
24
|
+
line(`export default {`);
|
|
25
|
+
indent(() => {
|
|
26
|
+
line('meta(){');
|
|
27
|
+
indent(() => {
|
|
28
|
+
line(`this.schedule('* * * * *'); // run every minute`);
|
|
29
|
+
});
|
|
30
|
+
line('}');
|
|
31
|
+
});
|
|
32
|
+
line();
|
|
33
|
+
indent(() => {
|
|
34
|
+
line('run(){');
|
|
35
|
+
indent(() => {
|
|
36
|
+
line(`console.log('${this.inflector.dasherize(normalizedName)} background job coming soon!')`);
|
|
37
|
+
});
|
|
38
|
+
line('}');
|
|
39
|
+
});
|
|
40
|
+
line('};');
|
|
41
|
+
line();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
export default {
|
|
4
4
|
async run(){
|
|
5
|
-
const
|
|
5
|
+
const { name = '' } = this.params;
|
|
6
6
|
const normalizedName = this.inflector.snakeify(name);
|
|
7
7
|
if(normalizedName == ''){
|
|
8
|
-
console.error('A command name must be given.');
|
|
8
|
+
console.error('A command --name must be given.');
|
|
9
9
|
process.exit();
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
|
|
2
2
|
export default {
|
|
3
3
|
async run(){
|
|
4
|
-
const
|
|
5
|
-
const name = this.inflector.snakeify(extractArg(''));
|
|
4
|
+
const name = this.inflector.snakeify(this.params.name || '');
|
|
6
5
|
if(name == ''){
|
|
7
|
-
console.error('A component name must be given.');
|
|
6
|
+
console.error('A component --name must be given.');
|
|
8
7
|
process.exit();
|
|
9
8
|
}
|
|
10
9
|
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
|
|
2
2
|
export default {
|
|
3
3
|
async run(){
|
|
4
|
-
const { extractArg, extractFields, extractOptions } = this.cliUtils;
|
|
5
4
|
|
|
6
|
-
const suffix = this.inflector.snakeify(
|
|
7
|
-
|
|
8
|
-
const {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
const suffix = this.inflector.snakeify(this.params.suffix || 'migration');
|
|
6
|
+
|
|
7
|
+
const { fields = '' } = this.params;
|
|
8
|
+
const normalizedFields = this.cliUtils.normalizeFields(fields);
|
|
9
|
+
|
|
10
|
+
const table = this.params.table || (() => {
|
|
11
|
+
const matches = suffix.match(/_to_(.+)$/);
|
|
12
|
+
if(matches){
|
|
13
|
+
return matches[1];
|
|
14
|
+
}
|
|
15
|
+
})();
|
|
16
16
|
|
|
17
17
|
const unixTime = Math.floor(new Date().getTime() / 1000);
|
|
18
18
|
const name = `${unixTime}_${suffix}`;
|
|
@@ -33,10 +33,10 @@ export default {
|
|
|
33
33
|
indent(() => {
|
|
34
34
|
line(`async migrate(){`);
|
|
35
35
|
indent(() => {
|
|
36
|
-
if(table &&
|
|
36
|
+
if(table && normalizedFields.length){
|
|
37
37
|
line(`await this.database.table('${table}', async ${table} => {`);
|
|
38
38
|
indent(() => {
|
|
39
|
-
|
|
39
|
+
normalizedFields.forEach(({ name, type }) => {
|
|
40
40
|
line(`await ${table}.addColumn('${name}', '${type}');`);
|
|
41
41
|
});
|
|
42
42
|
})
|
|
@@ -2,20 +2,17 @@
|
|
|
2
2
|
export default {
|
|
3
3
|
async run(){
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
const name = this.inflector.snakeify(extractArg(''));
|
|
5
|
+
const name = this.inflector.snakeify(this.params.name || '');
|
|
7
6
|
if(name == ''){
|
|
8
|
-
console.error('A model name must be given.');
|
|
7
|
+
console.error('A model --name must be given.');
|
|
9
8
|
process.exit();
|
|
10
9
|
}
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
|
|
11
|
+
let { fields = '' } = this.params;
|
|
12
|
+
|
|
13
13
|
const collectionName = this.inflector.camelize(this.inflector.pluralize(name));
|
|
14
14
|
if(!await this.database[collectionName]){
|
|
15
|
-
|
|
16
|
-
return `${ mandatory ? '^' : '' }${name}:${type}`
|
|
17
|
-
});
|
|
18
|
-
await this.runCommand('generate-migration', `create_${name}`, ...denormalizedFields, '--table', collectionName)
|
|
15
|
+
await this.runCommand('generate-migration', { name: `create_${name}`, fields, table: collectionName});
|
|
19
16
|
}
|
|
20
17
|
|
|
21
18
|
const { inProjectRootDir, generateFile, line, indent } = this.fsBuilder;
|
|
@@ -7,18 +7,23 @@ const defaultDependencies = [
|
|
|
7
7
|
];
|
|
8
8
|
|
|
9
9
|
export default {
|
|
10
|
-
|
|
10
|
+
meta(){
|
|
11
|
+
this.assignProps({
|
|
12
|
+
external: true,
|
|
13
|
+
internal: false
|
|
14
|
+
});
|
|
15
|
+
},
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
const name =
|
|
17
|
+
async run(){
|
|
18
|
+
const name = this.params.name || '';
|
|
14
19
|
if(name == ''){
|
|
15
|
-
console.error('A project name must be given.');
|
|
20
|
+
console.error('A project --name must be given.');
|
|
16
21
|
process.exit();
|
|
17
22
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
|
|
24
|
+
let { with: dependencies = '', core = false } = this.params;
|
|
25
|
+
|
|
26
|
+
dependencies = dependencies.split(/\s+/).map(dependency => dependency.trim());
|
|
22
27
|
|
|
23
28
|
if(!core) defaultDependencies.forEach(dependency => {
|
|
24
29
|
if(!dependencies.includes(dependency)) dependencies.unshift(dependency);
|
|
@@ -134,7 +139,7 @@ export default {
|
|
|
134
139
|
line();
|
|
135
140
|
});
|
|
136
141
|
|
|
137
|
-
spawnSync('
|
|
142
|
+
spawnSync('npm', [ 'install', ...dependencies ], {
|
|
138
143
|
stdio: 'inherit'
|
|
139
144
|
});
|
|
140
145
|
});
|
|
@@ -6,9 +6,7 @@ import { App, View } from 'pinstripe';
|
|
|
6
6
|
export default {
|
|
7
7
|
async run(){
|
|
8
8
|
|
|
9
|
-
const {
|
|
10
|
-
|
|
11
|
-
const { app = 'main' } = extractOptions();
|
|
9
|
+
const { app = 'main' } = this.params;
|
|
12
10
|
|
|
13
11
|
const { viewNames } = View.mapperFor(App.create(app, this.context).compose());
|
|
14
12
|
|
|
@@ -4,10 +4,10 @@ import { readFile } from 'fs/promises';
|
|
|
4
4
|
|
|
5
5
|
export default {
|
|
6
6
|
async run(){
|
|
7
|
-
const
|
|
7
|
+
const { name = '' } = this.params;
|
|
8
8
|
let normalizedName = name.replace(/^\//, '');
|
|
9
9
|
if(name == ''){
|
|
10
|
-
console.error('A view name must be given.');
|
|
10
|
+
console.error('A view --name must be given.');
|
|
11
11
|
process.exit();
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { BackgroundJob } from 'pinstripe';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
run(){
|
|
7
|
+
console.log('');
|
|
8
|
+
console.log('The following background jobs are available:');
|
|
9
|
+
console.log('');
|
|
10
|
+
BackgroundJob.names.forEach(backgroundJobName => {
|
|
11
|
+
console.log(` * ${chalk.green(backgroundJobName)}`);
|
|
12
|
+
});
|
|
13
|
+
console.log('');
|
|
14
|
+
}
|
|
15
|
+
};
|
|
@@ -4,9 +4,7 @@ import { App, View } from 'pinstripe';
|
|
|
4
4
|
|
|
5
5
|
export default {
|
|
6
6
|
run(){
|
|
7
|
-
const {
|
|
8
|
-
|
|
9
|
-
const { app = 'main' } = extractOptions();
|
|
7
|
+
const { app = 'main' } = this.params;
|
|
10
8
|
|
|
11
9
|
const { viewNames } = View.mapperFor(App.create(app, this.context).compose());
|
|
12
10
|
console.log('');
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
export default {
|
|
3
3
|
run(){
|
|
4
|
-
const {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
withoutBot: false
|
|
9
|
-
});
|
|
10
|
-
|
|
4
|
+
const {
|
|
5
|
+
app = `main:${process.env.HOST || '127.0.0.1'}:${parseInt(process.env.PORT || '3000')}`,
|
|
6
|
+
withoutBot = false
|
|
7
|
+
} = this.params;
|
|
11
8
|
|
|
12
9
|
const apps = [];
|
|
13
10
|
let currentPort = 3000;
|
package/lib/component.js
CHANGED
|
@@ -203,7 +203,7 @@ export const Component = Class.extend().include({
|
|
|
203
203
|
},
|
|
204
204
|
|
|
205
205
|
get isInput(){
|
|
206
|
-
return this.is('input, textarea');
|
|
206
|
+
return this.is('input, textarea, select');
|
|
207
207
|
},
|
|
208
208
|
|
|
209
209
|
get name(){
|
|
@@ -220,6 +220,9 @@ export const Component = Class.extend().include({
|
|
|
220
220
|
if(this.is('input[type="checkbox"]')){
|
|
221
221
|
return this.is(':checked') ? true : false;
|
|
222
222
|
}
|
|
223
|
+
if(this.is('select')){
|
|
224
|
+
return this.findAll('option').map(option => option.value)[this.node.selectedIndex];
|
|
225
|
+
}
|
|
223
226
|
return this.node.value;
|
|
224
227
|
},
|
|
225
228
|
|
|
@@ -597,14 +600,14 @@ function patchAttributes(attributes){
|
|
|
597
600
|
if(attributes[key] === undefined){
|
|
598
601
|
Element.prototype.removeAttribute.call(this.node, key); // work around for https://github.com/cypress-io/cypress/issues/26206
|
|
599
602
|
// this.node.removeAttribute(key);
|
|
603
|
+
if(key == 'checked') this.node.checked = false;
|
|
600
604
|
}
|
|
601
605
|
})
|
|
602
606
|
Object.keys(attributes).forEach((key) => {
|
|
603
607
|
if(!currentAttributes.hasOwnProperty(key) || currentAttributes[key] != attributes[key]){
|
|
604
608
|
this.node.setAttribute(key, attributes[key]);
|
|
605
|
-
if(key == 'value')
|
|
606
|
-
|
|
607
|
-
}
|
|
609
|
+
if(key == 'value') this.node.value = attributes[key];
|
|
610
|
+
if(key == 'checked') this.node.checked = true;
|
|
608
611
|
}
|
|
609
612
|
})
|
|
610
613
|
}
|
|
@@ -51,11 +51,23 @@ export function normalizeUrl(url, referenceUrl = window.location){
|
|
|
51
51
|
const matches = `${url}`.match(/^&(.*)$/);
|
|
52
52
|
const out = matches ? new URL(referenceUrl) : new URL(url, referenceUrl);
|
|
53
53
|
if(matches){
|
|
54
|
-
|
|
55
|
-
out.search
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
54
|
+
out.search = `?${stringifyUrlSearch({
|
|
55
|
+
...parseUrlSearch(out.search),
|
|
56
|
+
...parseUrlSearch(`${matches[1]}`)
|
|
57
|
+
})}`;
|
|
59
58
|
}
|
|
60
59
|
return out;
|
|
61
|
-
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function parseUrlSearch(search){
|
|
63
|
+
const out = {};
|
|
64
|
+
search.replace(/^\?/, '').split('&').forEach(pair => {
|
|
65
|
+
const [key, value] = pair.split('=');
|
|
66
|
+
out[decodeURIComponent(key)] = decodeURIComponent(value);
|
|
67
|
+
});
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function stringifyUrlSearch(search){
|
|
72
|
+
return Object.keys(search).map(key => `${encodeURIComponent(key)}=${encodeURIComponent(search[key])}`).join('&');
|
|
73
|
+
}
|
|
@@ -47,6 +47,10 @@ export default {
|
|
|
47
47
|
return this.body.progressBar;
|
|
48
48
|
},
|
|
49
49
|
|
|
50
|
+
get loadCacheNamespace(){
|
|
51
|
+
return this.head.find('meta[name="pinstripe-load-cache-namespace"]')?.params.content ?? 'default';
|
|
52
|
+
},
|
|
53
|
+
|
|
50
54
|
async load(url = this.url.toString(), options = {}){
|
|
51
55
|
const { replace, method = 'GET' } = options;
|
|
52
56
|
const previousUrl = this.url.toString();
|
|
@@ -65,12 +69,12 @@ export default {
|
|
|
65
69
|
},
|
|
66
70
|
|
|
67
71
|
async preload(url){
|
|
68
|
-
if(loadCache.get(url
|
|
72
|
+
if(loadCache.get(`${this.document.loadCacheNamespace}:${url}`)) return;
|
|
69
73
|
if(preloading[url.toString()]) return;
|
|
70
74
|
preloading[url.toString()] = true;
|
|
71
75
|
const response = await fetch(url);
|
|
72
76
|
const html = await response.text();
|
|
73
|
-
loadCache.put(url
|
|
77
|
+
loadCache.put(`${this.document.loadCacheNamespace}:${url}`, html);
|
|
74
78
|
delete preloading[url.toString()];
|
|
75
79
|
}
|
|
76
80
|
};
|