@youcan/app 2.1.3 → 2.2.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 (30) hide show
  1. package/dist/cli/commands/app/dev.d.ts +14 -6
  2. package/dist/cli/commands/app/dev.js +81 -48
  3. package/dist/cli/commands/app/generate/extension.d.ts +6 -6
  4. package/dist/cli/commands/app/generate/extension.js +49 -49
  5. package/dist/cli/commands/app/install.d.ts +5 -5
  6. package/dist/cli/commands/app/install.js +20 -20
  7. package/dist/cli/services/dev/workers/app-worker.d.ts +11 -10
  8. package/dist/cli/services/dev/workers/app-worker.js +30 -30
  9. package/dist/cli/services/dev/workers/index.d.ts +11 -10
  10. package/dist/cli/services/dev/workers/index.js +18 -18
  11. package/dist/cli/services/dev/workers/theme-extension-worker.d.ts +15 -15
  12. package/dist/cli/services/dev/workers/theme-extension-worker.js +113 -113
  13. package/dist/cli/services/dev/workers/web-worker.d.ts +11 -11
  14. package/dist/cli/services/dev/workers/web-worker.js +23 -23
  15. package/dist/cli/services/generate/extensions/index.d.ts +2 -2
  16. package/dist/cli/services/generate/extensions/index.js +2 -2
  17. package/dist/cli/services/generate/extensions/theme-extension.d.ts +3 -3
  18. package/dist/cli/services/generate/extensions/theme-extension.js +17 -17
  19. package/dist/cli/services/generate/generate.d.ts +10 -10
  20. package/dist/cli/services/generate/generate.js +24 -24
  21. package/dist/constants.d.ts +5 -5
  22. package/dist/constants.js +4 -4
  23. package/dist/flags.d.ts +3 -3
  24. package/dist/flags.js +7 -7
  25. package/dist/types.d.ts +69 -69
  26. package/dist/util/app-loader.d.ts +2 -2
  27. package/dist/util/app-loader.js +35 -35
  28. package/dist/util/theme-command.d.ts +3 -3
  29. package/dist/util/theme-command.js +1 -1
  30. package/package.json +2 -2
@@ -1,6 +1,14 @@
1
- import { AppCommand } from '@/util/theme-command';
2
- declare class Dev extends AppCommand {
3
- static description: string;
4
- run(): Promise<any>;
5
- }
6
- export default Dev;
1
+ import { AppCommand } from '@/util/theme-command';
2
+ declare class Dev extends AppCommand {
3
+ static description: string;
4
+ private app;
5
+ private session;
6
+ private readonly hotKeys;
7
+ run(): Promise<any>;
8
+ reloadWorkers(): Promise<void>;
9
+ private runWorkers;
10
+ private syncAppConfig;
11
+ private prepareDevProcesses;
12
+ private openAppPreview;
13
+ }
14
+ export default Dev;
@@ -1,56 +1,89 @@
1
- import { Session, Tasks, Env, Http, Filesystem, Path } from '@youcan/cli-kit';
1
+ import { Session, Tasks, UI, Env, Http, Filesystem, Path, System } from '@youcan/cli-kit';
2
2
  import { AppCommand } from '../../../util/theme-command.js';
3
3
  import { load } from '../../../util/app-loader.js';
4
4
  import { APP_CONFIG_FILENAME } from '../../../constants.js';
5
5
  import { bootAppWorker, bootWebWorker, bootExtensionWorker } from '../../services/dev/workers/index.js';
6
6
 
7
- class Dev extends AppCommand {
8
- static description = 'Run the app in dev mode';
9
- async run() {
10
- const app = await load();
11
- const session = await Session.authenticate(this);
12
- const { workers } = await Tasks.run({ cmd: this, workers: [] }, [
13
- {
14
- title: 'Syncing app configuration..',
15
- async task() {
16
- const endpoint = app.config.id == null
17
- ? `${Env.apiHostname()}/apps/create`
18
- : `${Env.apiHostname()}/apps/${app.config.id}/update`;
19
- const res = await Http.post(endpoint, {
20
- headers: { Authorization: `Bearer ${session.access_token}` },
21
- body: JSON.stringify({
22
- name: app.config.name,
23
- app_url: app.config.app_url,
24
- redirect_urls: app.config.redirect_urls,
25
- }),
26
- });
27
- app.config = {
28
- name: res.name,
29
- id: res.id,
30
- app_url: res.app_url,
31
- redirect_urls: res.redirect_urls,
32
- oauth: {
33
- scopes: res.scopes,
34
- client_id: res.client_id,
35
- },
36
- };
37
- await Filesystem.writeJsonFile(Path.join(app.root, APP_CONFIG_FILENAME), app.config);
38
- },
39
- },
40
- {
41
- title: 'Preparing dev processes...',
42
- async task(ctx) {
43
- const promises = [
44
- bootAppWorker(ctx.cmd, app),
45
- ];
46
- app.webs.forEach(web => promises.unshift(bootWebWorker(ctx.cmd, app, web)));
47
- app.extensions.forEach(ext => promises.unshift(bootExtensionWorker(ctx.cmd, app, ext)));
48
- ctx.workers = await Promise.all(promises);
49
- },
50
- },
51
- ]);
52
- await Promise.all(workers.map(async (worker) => await worker.run()));
53
- }
7
+ class Dev extends AppCommand {
8
+ static description = 'Run the app in dev mode';
9
+ app;
10
+ session;
11
+ hotKeys = [
12
+ {
13
+ keyboardKey: 'p',
14
+ description: 'preview in your dev store',
15
+ handler: async () => this.openAppPreview(),
16
+ },
17
+ {
18
+ keyboardKey: 'q',
19
+ description: 'quit',
20
+ handler: () => this.exit(0),
21
+ },
22
+ ];
23
+ async run() {
24
+ this.session = await Session.authenticate(this);
25
+ this.app = await load();
26
+ const { workers } = await Tasks.run({ cmd: this, workers: [] }, [
27
+ {
28
+ title: 'Syncing app configuration..',
29
+ task: async () => await this.syncAppConfig(),
30
+ },
31
+ {
32
+ title: 'Preparing dev processes...',
33
+ task: async (ctx) => {
34
+ ctx.workers = await this.prepareDevProcesses();
35
+ },
36
+ },
37
+ ]);
38
+ UI.renderDevOutput({ hotKeys: this.hotKeys, cmd: this });
39
+ this.runWorkers(workers);
40
+ }
41
+ async reloadWorkers() {
42
+ this.controller = new AbortController();
43
+ this.app = await load();
44
+ await this.syncAppConfig();
45
+ await this.runWorkers(await this.prepareDevProcesses());
46
+ }
47
+ async runWorkers(workers) {
48
+ await Promise.all(workers.map(worker => worker.run())).catch(_ => { });
49
+ }
50
+ async syncAppConfig() {
51
+ const endpoint = this.app.config.id == null
52
+ ? `${Env.apiHostname()}/apps/create`
53
+ : `${Env.apiHostname()}/apps/${this.app.config.id}/update`;
54
+ const res = await Http.post(endpoint, {
55
+ headers: { Authorization: `Bearer ${this.session.access_token}` },
56
+ body: JSON.stringify({
57
+ name: this.app.config.name,
58
+ app_url: this.app.config.app_url,
59
+ redirect_urls: this.app.config.redirect_urls,
60
+ }),
61
+ });
62
+ this.app.config = {
63
+ name: res.name,
64
+ id: res.id,
65
+ app_url: res.app_url,
66
+ redirect_urls: res.redirect_urls,
67
+ oauth: {
68
+ scopes: res.scopes,
69
+ client_id: res.client_id,
70
+ },
71
+ };
72
+ await Filesystem.writeJsonFile(Path.join(this.app.root, APP_CONFIG_FILENAME), this.app.config);
73
+ }
74
+ async prepareDevProcesses() {
75
+ const promises = [
76
+ bootAppWorker(this, this.app),
77
+ ];
78
+ this.app.webs.forEach(web => promises.unshift(bootWebWorker(this, this.app, web)));
79
+ this.app.extensions.forEach(ext => promises.unshift(bootExtensionWorker(this, this.app, ext)));
80
+ return Promise.all(promises);
81
+ }
82
+ async openAppPreview() {
83
+ const endpointUrl = `${Env.apiHostname()}/apps/${this.app.config.id}/authorization-url`;
84
+ const { url } = await Http.get(endpointUrl);
85
+ System.open(url);
86
+ }
54
87
  }
55
88
 
56
89
  export { Dev as default };
@@ -1,6 +1,6 @@
1
- import { AppCommand } from '@/util/theme-command';
2
- declare class GenerateExtension extends AppCommand {
3
- static description: string;
4
- run(): Promise<any>;
5
- }
6
- export default GenerateExtension;
1
+ import { AppCommand } from '@/util/theme-command';
2
+ declare class GenerateExtension extends AppCommand {
3
+ static description: string;
4
+ run(): Promise<any>;
5
+ }
6
+ export default GenerateExtension;
@@ -4,55 +4,55 @@ import extensions from '../../../services/generate/extensions/index.js';
4
4
  import { ensureExtensionDirectoryExists, initThemeExtension } from '../../../services/generate/generate.js';
5
5
  import { APP_CONFIG_FILENAME } from '../../../../constants.js';
6
6
 
7
- class GenerateExtension extends AppCommand {
8
- static description = 'Generate an app extension';
9
- async run() {
10
- const filepath = Path.resolve(Path.cwd(), APP_CONFIG_FILENAME);
11
- const app = await Filesystem.readJsonFile(filepath);
12
- const { identifier } = await this.prompt({
13
- name: 'identifier',
14
- message: 'Extension type?',
15
- type: 'select',
16
- choices: extensions.map(ext => ({
17
- title: ext.name,
18
- value: ext.identifier,
19
- description: ext.description,
20
- })),
21
- });
22
- // TODO: prompt for extension type and flavor
23
- const extension = extensions.find(ext => ext.identifier === identifier);
24
- const type = extension.types[0];
25
- const flavor = type.flavors[0];
26
- const { name } = await this.prompt({
27
- name: 'name',
28
- message: 'Extension name?',
29
- type: 'text',
30
- initial: extension.name,
31
- validate: prev => prev.length >= 3,
32
- format: name => String.hyphenate(name),
33
- });
34
- await Tasks.run({}, [
35
- {
36
- title: 'Validating extension options..',
37
- async task(ctx) {
38
- ctx.directory = await ensureExtensionDirectoryExists(name);
39
- },
40
- },
41
- {
42
- title: 'Initializing extension..',
43
- async task(ctx) {
44
- await initThemeExtension({
45
- app,
46
- name,
47
- type,
48
- flavor,
49
- directory: ctx.directory,
50
- });
51
- },
52
- },
53
- ]);
54
- this.output.info(`Extension '${name}' successfully generated.`);
55
- }
7
+ class GenerateExtension extends AppCommand {
8
+ static description = 'Generate an app extension';
9
+ async run() {
10
+ const filepath = Path.resolve(Path.cwd(), APP_CONFIG_FILENAME);
11
+ const app = await Filesystem.readJsonFile(filepath);
12
+ const { identifier } = await this.prompt({
13
+ name: 'identifier',
14
+ message: 'Extension type?',
15
+ type: 'select',
16
+ choices: extensions.map(ext => ({
17
+ title: ext.name,
18
+ value: ext.identifier,
19
+ description: ext.description,
20
+ })),
21
+ });
22
+ // TODO: prompt for extension type and flavor
23
+ const extension = extensions.find(ext => ext.identifier === identifier);
24
+ const type = extension.types[0];
25
+ const flavor = type.flavors[0];
26
+ const { name } = await this.prompt({
27
+ name: 'name',
28
+ message: 'Extension name?',
29
+ type: 'text',
30
+ initial: extension.name,
31
+ validate: prev => prev.length >= 3,
32
+ format: name => String.hyphenate(name),
33
+ });
34
+ await Tasks.run({}, [
35
+ {
36
+ title: 'Validating extension options..',
37
+ async task(ctx) {
38
+ ctx.directory = await ensureExtensionDirectoryExists(name);
39
+ },
40
+ },
41
+ {
42
+ title: 'Initializing extension..',
43
+ async task(ctx) {
44
+ await initThemeExtension({
45
+ app,
46
+ name,
47
+ type,
48
+ flavor,
49
+ directory: ctx.directory,
50
+ });
51
+ },
52
+ },
53
+ ]);
54
+ this.output.info(`Extension '${name}' successfully generated.`);
55
+ }
56
56
  }
57
57
 
58
58
  export { GenerateExtension as default };
@@ -1,5 +1,5 @@
1
- import { AppCommand } from '@/util/theme-command';
2
- export default class Install extends AppCommand {
3
- static description: string;
4
- run(): Promise<void>;
5
- }
1
+ import { AppCommand } from '@/util/theme-command';
2
+ export default class Install extends AppCommand {
3
+ static description: string;
4
+ run(): Promise<void>;
5
+ }
@@ -2,26 +2,26 @@ import { Session, Tasks, Http, Env, System } from '@youcan/cli-kit';
2
2
  import { load } from '../../../util/app-loader.js';
3
3
  import { AppCommand } from '../../../util/theme-command.js';
4
4
 
5
- class Install extends AppCommand {
6
- static description = 'Generate an app installation URL';
7
- async run() {
8
- const app = await load();
9
- if (!app.config.id) {
10
- this.output.error('Please run the `dev` command before installing your app.');
11
- }
12
- await Session.authenticate(this);
13
- const { url } = await Tasks.run({}, [
14
- {
15
- title: 'Building authorization url',
16
- async task(ctx) {
17
- const { url } = await Http.get(`${Env.apiHostname()}/apps/${app.config.id}/authorization-url`);
18
- ctx.url = url;
19
- },
20
- },
21
- ]);
22
- await this.output.anykey('Press any key to open the installation page in your browser');
23
- System.open(url);
24
- }
5
+ class Install extends AppCommand {
6
+ static description = 'Generate an app installation URL';
7
+ async run() {
8
+ const app = await load();
9
+ if (!app.config.id) {
10
+ this.output.error('Please run the `dev` command before installing your app.');
11
+ }
12
+ await Session.authenticate(this);
13
+ const { url } = await Tasks.run({}, [
14
+ {
15
+ title: 'Building authorization url',
16
+ async task(ctx) {
17
+ const { url } = await Http.get(`${Env.apiHostname()}/apps/${app.config.id}/authorization-url`);
18
+ ctx.url = url;
19
+ },
20
+ },
21
+ ]);
22
+ await this.output.anykey('Press any key to open the installation page in your browser');
23
+ System.open(url);
24
+ }
25
25
  }
26
26
 
27
27
  export { Install as default };
@@ -1,10 +1,11 @@
1
- import { type Cli, Worker } from '@youcan/cli-kit';
2
- import type { App } from '@/types';
3
- export default class AppWorker extends Worker.Abstract {
4
- private command;
5
- private app;
6
- private logger;
7
- constructor(command: Cli.Command, app: App);
8
- boot(): Promise<void>;
9
- run(): Promise<void>;
10
- }
1
+ import { Worker } from '@youcan/cli-kit';
2
+ import type { App } from '@/types';
3
+ import type DevCommand from '@/cli/commands/app/dev';
4
+ export default class AppWorker extends Worker.Abstract {
5
+ private command;
6
+ private app;
7
+ private logger;
8
+ constructor(command: DevCommand, app: App);
9
+ boot(): Promise<void>;
10
+ run(): Promise<void>;
11
+ }
@@ -1,35 +1,35 @@
1
- import { Worker, Color, Filesystem, Path } from '@youcan/cli-kit';
1
+ import { Worker, Filesystem, Path } from '@youcan/cli-kit';
2
2
  import { APP_CONFIG_FILENAME } from '../../../../constants.js';
3
3
 
4
- class AppWorker extends Worker.Abstract {
5
- command;
6
- app;
7
- logger;
8
- constructor(command, app) {
9
- super();
10
- this.command = command;
11
- this.app = app;
12
- this.logger = new Worker.Logger('stdout', 'app', Color.cyan);
13
- }
14
- async boot() {
15
- }
16
- async run() {
17
- await this.command.output.wait(500);
18
- this.logger.write('watching for config updates...');
19
- const watcher = Filesystem.watch(Path.resolve(this.app.root, APP_CONFIG_FILENAME), {
20
- persistent: true,
21
- ignoreInitial: true,
22
- awaitWriteFinish: {
23
- stabilityThreshold: 50,
24
- },
25
- });
26
- watcher.once('change', async () => {
27
- await watcher.close();
28
- this.command.controller.abort();
29
- this.logger.write('config update detected, reloading workers...');
30
- this.command.config.runCommand(this.command.id, this.command.argv);
31
- });
32
- }
4
+ class AppWorker extends Worker.Abstract {
5
+ command;
6
+ app;
7
+ logger;
8
+ constructor(command, app) {
9
+ super();
10
+ this.command = command;
11
+ this.app = app;
12
+ this.logger = new Worker.Logger('app', 'green');
13
+ }
14
+ async boot() {
15
+ }
16
+ async run() {
17
+ await this.command.output.wait(500);
18
+ this.logger.write('watching for config updates...');
19
+ const watcher = Filesystem.watch(Path.resolve(this.app.root, APP_CONFIG_FILENAME), {
20
+ persistent: true,
21
+ ignoreInitial: true,
22
+ awaitWriteFinish: {
23
+ stabilityThreshold: 50,
24
+ },
25
+ });
26
+ watcher.once('change', async () => {
27
+ await watcher.close();
28
+ this.logger.write('config update detected, reloading workers...');
29
+ this.command.controller.abort();
30
+ this.command.reloadWorkers();
31
+ });
32
+ }
33
33
  }
34
34
 
35
35
  export { AppWorker as default };
@@ -1,10 +1,11 @@
1
- import type { Cli, Worker } from '@youcan/cli-kit';
2
- import WebWorker from './web-worker';
3
- import AppWorker from './app-worker';
4
- import type { App, Extension, Web } from '@/types';
5
- export interface ExtensionWorkerCtor {
6
- new (command: Cli.Command, app: App, extension: Extension): Worker.Interface;
7
- }
8
- export declare function bootAppWorker(command: Cli.Command, app: App): Promise<AppWorker>;
9
- export declare function bootExtensionWorker(command: Cli.Command, app: App, extension: Extension): Promise<Worker.Interface>;
10
- export declare function bootWebWorker(command: Cli.Command, app: App, web: Web): Promise<WebWorker>;
1
+ import type { Cli, Worker } from '@youcan/cli-kit';
2
+ import WebWorker from './web-worker';
3
+ import AppWorker from './app-worker';
4
+ import type { App, Extension, Web } from '@/types';
5
+ import type DevCommand from '@/cli/commands/app/dev';
6
+ export interface ExtensionWorkerCtor {
7
+ new (command: Cli.Command, app: App, extension: Extension): Worker.Interface;
8
+ }
9
+ export declare function bootAppWorker(command: DevCommand, app: App): Promise<AppWorker>;
10
+ export declare function bootExtensionWorker(command: Cli.Command, app: App, extension: Extension): Promise<Worker.Interface>;
11
+ export declare function bootWebWorker(command: Cli.Command, app: App, web: Web): Promise<WebWorker>;
@@ -2,24 +2,24 @@ import ThemeExtensionWorker from './theme-extension-worker.js';
2
2
  import WebWorker from './web-worker.js';
3
3
  import AppWorker from './app-worker.js';
4
4
 
5
- const EXTENSION_WORKERS = {
6
- theme: ThemeExtensionWorker,
7
- };
8
- async function bootAppWorker(command, app) {
9
- const worker = new AppWorker(command, app);
10
- await worker.boot();
11
- return worker;
12
- }
13
- async function bootExtensionWorker(command, app, extension) {
14
- const Ctor = EXTENSION_WORKERS[extension.config.type];
15
- const worker = new Ctor(command, app, extension);
16
- await worker.boot();
17
- return worker;
18
- }
19
- async function bootWebWorker(command, app, web) {
20
- const worker = new WebWorker(command, app, web);
21
- await worker.boot();
22
- return worker;
5
+ const EXTENSION_WORKERS = {
6
+ theme: ThemeExtensionWorker,
7
+ };
8
+ async function bootAppWorker(command, app) {
9
+ const worker = new AppWorker(command, app);
10
+ await worker.boot();
11
+ return worker;
12
+ }
13
+ async function bootExtensionWorker(command, app, extension) {
14
+ const Ctor = EXTENSION_WORKERS[extension.config.type];
15
+ const worker = new Ctor(command, app, extension);
16
+ await worker.boot();
17
+ return worker;
18
+ }
19
+ async function bootWebWorker(command, app, web) {
20
+ const worker = new WebWorker(command, app, web);
21
+ await worker.boot();
22
+ return worker;
23
23
  }
24
24
 
25
25
  export { bootAppWorker, bootExtensionWorker, bootWebWorker };
@@ -1,15 +1,15 @@
1
- import type { Cli } from '@youcan/cli-kit';
2
- import { Worker } from '@youcan/cli-kit';
3
- import type { App, Extension } from '@/types';
4
- export default class ThemeExtensionWorker extends Worker.Abstract {
5
- private command;
6
- private app;
7
- private extension;
8
- private logger;
9
- private queue;
10
- FILE_TYPES: string[];
11
- constructor(command: Cli.Command, app: App, extension: Extension);
12
- boot(): Promise<void>;
13
- run(): Promise<void>;
14
- private enqueue;
15
- }
1
+ import type { Cli } from '@youcan/cli-kit';
2
+ import { Worker } from '@youcan/cli-kit';
3
+ import type { App, Extension } from '@/types';
4
+ export default class ThemeExtensionWorker extends Worker.Abstract {
5
+ private command;
6
+ private app;
7
+ private extension;
8
+ private logger;
9
+ private queue;
10
+ FILE_TYPES: string[];
11
+ constructor(command: Cli.Command, app: App, extension: Extension);
12
+ boot(): Promise<void>;
13
+ run(): Promise<void>;
14
+ private enqueue;
15
+ }