@youcan/app 1.1.0-beta.2
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/LICENSE +21 -0
- package/dist/cli/commands/app/dev.d.ts +5 -0
- package/dist/cli/commands/app/dev.js +46 -0
- package/dist/cli/commands/app/generate/extension.d.ts +5 -0
- package/dist/cli/commands/app/generate/extension.js +57 -0
- package/dist/cli/services/dev/sync-extensions.d.ts +1 -0
- package/dist/cli/services/dev/sync-extensions.js +4 -0
- package/dist/cli/services/dev/workers/index.d.ts +3 -0
- package/dist/cli/services/dev/workers/index.js +13 -0
- package/dist/cli/services/dev/workers/theme-extension-worker.d.ts +15 -0
- package/dist/cli/services/dev/workers/theme-extension-worker.js +117 -0
- package/dist/cli/services/generate/extensions/index.d.ts +2 -0
- package/dist/cli/services/generate/extensions/index.js +7 -0
- package/dist/cli/services/generate/extensions/theme-extension.d.ts +3 -0
- package/dist/cli/services/generate/extensions/theme-extension.js +20 -0
- package/dist/cli/services/generate/generate.d.ts +10 -0
- package/dist/cli/services/generate/generate.js +30 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +5 -0
- package/dist/flags.d.ts +3 -0
- package/dist/flags.js +13 -0
- package/dist/types.d.ts +63 -0
- package/dist/types.js +1 -0
- package/dist/util/app-loader.d.ts +2 -0
- package/dist/util/app-loader.js +27 -0
- package/dist/util/theme-command.d.ts +3 -0
- package/dist/util/theme-command.js +6 -0
- package/package.json +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 YouCan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Session, Tasks, Http, Env, Filesystem, Path } from '@youcan/cli-kit';
|
|
2
|
+
import { AppCommand } from '../../../util/theme-command.js';
|
|
3
|
+
import { load } from '../../../util/app-loader.js';
|
|
4
|
+
import { APP_CONFIG_FILENAME } from '../../../constants.js';
|
|
5
|
+
import { bootExtensionWorker } from '../../services/dev/workers/index.js';
|
|
6
|
+
|
|
7
|
+
class Dev extends AppCommand {
|
|
8
|
+
async run() {
|
|
9
|
+
const app = await load();
|
|
10
|
+
const session = await Session.authenticate(this);
|
|
11
|
+
await Tasks.run({ cmd: this }, [
|
|
12
|
+
{
|
|
13
|
+
title: 'Creating draft app..',
|
|
14
|
+
skip() {
|
|
15
|
+
return app.config.id != null;
|
|
16
|
+
},
|
|
17
|
+
async task() {
|
|
18
|
+
const res = await Http.post(`${Env.apiHostname()}/apps/draft/create`, {
|
|
19
|
+
headers: { Authorization: `Bearer ${session.access_token}` },
|
|
20
|
+
body: JSON.stringify({ name: app.config.name }),
|
|
21
|
+
});
|
|
22
|
+
app.config = {
|
|
23
|
+
name: res.name,
|
|
24
|
+
id: res.id,
|
|
25
|
+
url: res.url,
|
|
26
|
+
oauth: {
|
|
27
|
+
scopes: res.scopes,
|
|
28
|
+
client_id: res.client_id,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
await Filesystem.writeJsonFile(Path.join(app.root, APP_CONFIG_FILENAME), app.config);
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
title: 'Preparing dev workers..',
|
|
36
|
+
async task(ctx) {
|
|
37
|
+
const promises = app.extensions.map(async (ext) => await bootExtensionWorker(ctx.cmd, app, ext));
|
|
38
|
+
const workers = await Promise.all(promises);
|
|
39
|
+
await Promise.all(workers.map(async (worker) => await worker.run()));
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
]);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export { Dev as default };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Path, Filesystem, String, Tasks } from '@youcan/cli-kit';
|
|
2
|
+
import { AppCommand } from '../../../../util/theme-command.js';
|
|
3
|
+
import extensions from '../../../services/generate/extensions/index.js';
|
|
4
|
+
import { ensureExtensionDirectoryExists, initThemeExtension } from '../../../services/generate/generate.js';
|
|
5
|
+
import { APP_CONFIG_FILENAME } from '../../../../constants.js';
|
|
6
|
+
|
|
7
|
+
class GenerateExtension extends AppCommand {
|
|
8
|
+
async run() {
|
|
9
|
+
const filepath = Path.resolve(Path.cwd(), APP_CONFIG_FILENAME);
|
|
10
|
+
const app = await Filesystem.readJsonFile(filepath);
|
|
11
|
+
const { identifier } = await this.prompt({
|
|
12
|
+
name: 'identifier',
|
|
13
|
+
message: 'Extension type?',
|
|
14
|
+
type: 'select',
|
|
15
|
+
choices: extensions.map(ext => ({
|
|
16
|
+
title: ext.name,
|
|
17
|
+
value: ext.identifier,
|
|
18
|
+
description: ext.description,
|
|
19
|
+
})),
|
|
20
|
+
});
|
|
21
|
+
// TODO: prompt for extension type and flavor
|
|
22
|
+
const extension = extensions.find(ext => ext.identifier === identifier);
|
|
23
|
+
const type = extension.types[0];
|
|
24
|
+
const flavor = type.flavors[0];
|
|
25
|
+
const { name } = await this.prompt({
|
|
26
|
+
name: 'name',
|
|
27
|
+
message: 'Extension name?',
|
|
28
|
+
type: 'text',
|
|
29
|
+
initial: extension.name,
|
|
30
|
+
validate: prev => prev.length >= 3,
|
|
31
|
+
format: name => String.hyphenate(name),
|
|
32
|
+
});
|
|
33
|
+
await Tasks.run({}, [
|
|
34
|
+
{
|
|
35
|
+
title: 'Validating extension options..',
|
|
36
|
+
async task(ctx) {
|
|
37
|
+
ctx.directory = await ensureExtensionDirectoryExists(name);
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
title: 'Initializing extension..',
|
|
42
|
+
async task(ctx) {
|
|
43
|
+
await initThemeExtension({
|
|
44
|
+
app,
|
|
45
|
+
name,
|
|
46
|
+
type,
|
|
47
|
+
flavor,
|
|
48
|
+
directory: ctx.directory,
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
]);
|
|
53
|
+
this.output.info(`Extension '${name}' successfully generated.`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { GenerateExtension as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function syncExtensions(): Promise<void>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import ThemeExtensionWorker from './theme-extension-worker.js';
|
|
2
|
+
|
|
3
|
+
const EXTENSION_WORKERS = {
|
|
4
|
+
theme: ThemeExtensionWorker,
|
|
5
|
+
};
|
|
6
|
+
async function bootExtensionWorker(command, app, extension) {
|
|
7
|
+
const Ctor = EXTENSION_WORKERS[extension.config.type];
|
|
8
|
+
const worker = new Ctor(command, app, extension);
|
|
9
|
+
await worker.boot();
|
|
10
|
+
return worker;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export { bootExtensionWorker };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Cli } from '@youcan/cli-kit';
|
|
2
|
+
import type { App, Extension, ExtensionWorker } from '@/types';
|
|
3
|
+
export default class ThemeExtensionWorker implements ExtensionWorker {
|
|
4
|
+
private command;
|
|
5
|
+
private app;
|
|
6
|
+
private extension;
|
|
7
|
+
FILE_TYPES: string[];
|
|
8
|
+
private EVENT_LOG_MAP;
|
|
9
|
+
private formatter;
|
|
10
|
+
constructor(command: Cli.Command, app: App, extension: Extension);
|
|
11
|
+
boot(): Promise<void>;
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
private file;
|
|
14
|
+
private log;
|
|
15
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Color, Session, Http, Env, Path, Filesystem, Crypto, Form } from '@youcan/cli-kit';
|
|
2
|
+
|
|
3
|
+
class ThemeExtensionWorker {
|
|
4
|
+
command;
|
|
5
|
+
app;
|
|
6
|
+
extension;
|
|
7
|
+
FILE_TYPES = [
|
|
8
|
+
'assets',
|
|
9
|
+
'locales',
|
|
10
|
+
'snippets',
|
|
11
|
+
'blocks',
|
|
12
|
+
];
|
|
13
|
+
EVENT_LOG_MAP = {
|
|
14
|
+
error: () => Color.bold().red('[error]'),
|
|
15
|
+
add: () => Color.bold().green('[created]'),
|
|
16
|
+
change: () => Color.bold().blue('[updated]'),
|
|
17
|
+
unlink: () => Color.bold().yellow('[deleted]'),
|
|
18
|
+
};
|
|
19
|
+
formatter = Intl.NumberFormat('en', {
|
|
20
|
+
unitDisplay: 'narrow',
|
|
21
|
+
notation: 'compact',
|
|
22
|
+
style: 'unit',
|
|
23
|
+
unit: 'byte',
|
|
24
|
+
});
|
|
25
|
+
constructor(command, app, extension) {
|
|
26
|
+
this.command = command;
|
|
27
|
+
this.app = app;
|
|
28
|
+
this.extension = extension;
|
|
29
|
+
}
|
|
30
|
+
async boot() {
|
|
31
|
+
const session = await Session.authenticate(this.command);
|
|
32
|
+
try {
|
|
33
|
+
const res = await Http.post(`${Env.apiHostname()}/apps/draft/${this.app.config.id}/extensions/create`, {
|
|
34
|
+
headers: { Authorization: `Bearer ${session.access_token}` },
|
|
35
|
+
body: JSON.stringify({ ...this.extension.config }),
|
|
36
|
+
});
|
|
37
|
+
this.extension.id = res.id;
|
|
38
|
+
this.extension.metadata = res.metadata;
|
|
39
|
+
for (const type of Object.keys(this.extension.metadata)) {
|
|
40
|
+
const descriptors = this.extension.metadata[type];
|
|
41
|
+
const directory = Path.resolve(this.extension.root, type);
|
|
42
|
+
const present = await Filesystem.readdir(Path.resolve(directory));
|
|
43
|
+
present.filter(f => !descriptors.find(d => d.file_name === f))
|
|
44
|
+
.forEach(async (file) => await this.file('put', type, file));
|
|
45
|
+
descriptors.forEach(async (descriptor) => {
|
|
46
|
+
const path = Path.resolve(directory, descriptor.file_name);
|
|
47
|
+
if (!(await Filesystem.exists(path))) {
|
|
48
|
+
return await this.file('del', type, descriptor.file_name);
|
|
49
|
+
}
|
|
50
|
+
const buff = await Filesystem.readFile(path);
|
|
51
|
+
if (Crypto.sha1(buff) !== descriptor.hash) {
|
|
52
|
+
await this.file('put', type, descriptor.file_name);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
this.command.error(err);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async run() {
|
|
62
|
+
const paths = this.FILE_TYPES
|
|
63
|
+
.map(p => Path.resolve(this.extension.root, p));
|
|
64
|
+
const watcher = Filesystem.watch(paths, {
|
|
65
|
+
persistent: true,
|
|
66
|
+
ignoreInitial: true,
|
|
67
|
+
awaitWriteFinish: {
|
|
68
|
+
stabilityThreshold: 50,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
watcher.on('all', async (event, path, stat) => {
|
|
72
|
+
try {
|
|
73
|
+
if (!['add', 'change', 'unlink'].includes(event)) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const start = new Date().getTime();
|
|
77
|
+
const [filetype, filename] = [
|
|
78
|
+
Path.basename(Path.dirname(path)),
|
|
79
|
+
Path.basename(path),
|
|
80
|
+
];
|
|
81
|
+
let size = 0;
|
|
82
|
+
switch (event) {
|
|
83
|
+
case 'add':
|
|
84
|
+
case 'change':
|
|
85
|
+
size = (await this.file('put', filetype, filename)).size;
|
|
86
|
+
break;
|
|
87
|
+
case 'unlink':
|
|
88
|
+
size = (await this.file('del', filetype, filename)).size;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
this.log(event, Path.join(filetype, filename), this.formatter.format(stat.size), new Date().getTime() - start);
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
this.log('error', path);
|
|
95
|
+
this.command.error(err);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
async file(op, type, name) {
|
|
100
|
+
const path = Path.resolve(this.extension.root, type, name);
|
|
101
|
+
return await Http.post(`${Env.apiHostname()}/apps/draft/${this.app.config.id}/extensions/${this.extension.id}/file`, {
|
|
102
|
+
body: Form.convert({
|
|
103
|
+
file_name: name,
|
|
104
|
+
file_type: type,
|
|
105
|
+
file_operation: op,
|
|
106
|
+
file_content: await Form.file(path),
|
|
107
|
+
}),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
log(event, path, size, time) {
|
|
111
|
+
const tag = this.EVENT_LOG_MAP[event]();
|
|
112
|
+
`${tag} ${Color.underline().white(path)}`;
|
|
113
|
+
this.command.log(`${tag} ${Color.underline().white(path)} - ${size} | ${time}ms \n`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export { ThemeExtensionWorker as default };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
var ThemeExtension = {
|
|
2
|
+
name: 'Theme extension',
|
|
3
|
+
identifier: 'theme-extension',
|
|
4
|
+
description: 'Liquid snippets to extend store themes',
|
|
5
|
+
types: [
|
|
6
|
+
{
|
|
7
|
+
url: 'https://github.com/youcan-shop/app-extension-templates',
|
|
8
|
+
type: 'theme',
|
|
9
|
+
flavors: [
|
|
10
|
+
{
|
|
11
|
+
name: 'Liquid',
|
|
12
|
+
value: 'liquid',
|
|
13
|
+
path: 'theme-extension/liquid',
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export { ThemeExtension as default };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ExtensionFlavor, ExtensionTemplateType, InitialAppConfig } from '@/types';
|
|
2
|
+
export declare function ensureExtensionDirectoryExists(name: string): Promise<string>;
|
|
3
|
+
export interface InitExtensionOptions {
|
|
4
|
+
name: string;
|
|
5
|
+
app: InitialAppConfig;
|
|
6
|
+
directory: string;
|
|
7
|
+
type: ExtensionTemplateType;
|
|
8
|
+
flavor?: ExtensionFlavor;
|
|
9
|
+
}
|
|
10
|
+
export declare function initThemeExtension(options: InitExtensionOptions): Promise<void>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Path, Filesystem, Git } from '@youcan/cli-kit';
|
|
2
|
+
import { EXTENSION_CONFIG_FILENAME } from '../../../constants.js';
|
|
3
|
+
|
|
4
|
+
async function ensureExtensionDirectoryExists(name) {
|
|
5
|
+
const dir = Path.join(Path.cwd(), 'extensions', name);
|
|
6
|
+
if (await Filesystem.exists(dir)) {
|
|
7
|
+
throw new Error(`The '${name}' already exists, choose a new name for your extension`);
|
|
8
|
+
}
|
|
9
|
+
await Filesystem.mkdir(dir);
|
|
10
|
+
return dir;
|
|
11
|
+
}
|
|
12
|
+
async function initThemeExtension(options) {
|
|
13
|
+
return Filesystem.tapIntoTmp(async (tmp) => {
|
|
14
|
+
const directory = Path.join(tmp, 'download');
|
|
15
|
+
await Filesystem.mkdir(directory);
|
|
16
|
+
await Git.clone({
|
|
17
|
+
url: options.type.url,
|
|
18
|
+
destination: directory,
|
|
19
|
+
shallow: true,
|
|
20
|
+
});
|
|
21
|
+
const flavorPath = Path.join(directory, options.flavor?.path || '');
|
|
22
|
+
if (!await Filesystem.exists(flavorPath)) {
|
|
23
|
+
throw new Error(`Extension flavor '${options.flavor?.name}' is unavailble`);
|
|
24
|
+
}
|
|
25
|
+
await Filesystem.move(flavorPath, options.directory, { overwrite: true });
|
|
26
|
+
await Filesystem.writeJsonFile(Path.join(options.directory, EXTENSION_CONFIG_FILENAME), { name: options.name, type: options.type.type });
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { ensureExtensionDirectoryExists, initThemeExtension };
|
package/dist/flags.d.ts
ADDED
package/dist/flags.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { Path } from '@youcan/cli-kit';
|
|
3
|
+
|
|
4
|
+
const APP_FLAGS = {
|
|
5
|
+
path: Flags.string({
|
|
6
|
+
env: 'YC_FLAG_PATH',
|
|
7
|
+
default: async () => Path.cwd(),
|
|
8
|
+
parse: async (input) => Path.resolve(input),
|
|
9
|
+
description: 'The path to your app directory.',
|
|
10
|
+
}),
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export { APP_FLAGS };
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Cli } from '@youcan/cli-kit';
|
|
2
|
+
export interface InitialAppConfig {
|
|
3
|
+
[key: string]: unknown;
|
|
4
|
+
name: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ExtensionWorkerConstructor<T extends Extension = Extension> {
|
|
7
|
+
new (command: Cli.Command, app: App, extension: T): ExtensionWorker;
|
|
8
|
+
}
|
|
9
|
+
export interface ExtensionWorker {
|
|
10
|
+
run(): Promise<void>;
|
|
11
|
+
boot(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
export type AppConfig = {
|
|
14
|
+
id: string;
|
|
15
|
+
url: string;
|
|
16
|
+
oauth: {
|
|
17
|
+
client_id: string;
|
|
18
|
+
scopes: string[];
|
|
19
|
+
};
|
|
20
|
+
} & InitialAppConfig;
|
|
21
|
+
export interface ExtensionConfig {
|
|
22
|
+
[key: string]: unknown;
|
|
23
|
+
type: string;
|
|
24
|
+
name: string;
|
|
25
|
+
}
|
|
26
|
+
export interface ExtensionFlavor {
|
|
27
|
+
name: string;
|
|
28
|
+
path?: string;
|
|
29
|
+
value: 'liquid';
|
|
30
|
+
}
|
|
31
|
+
export interface ExtensionTemplateType {
|
|
32
|
+
url: string;
|
|
33
|
+
type: string;
|
|
34
|
+
flavors: ExtensionFlavor[];
|
|
35
|
+
}
|
|
36
|
+
export interface ExtensionTemplate {
|
|
37
|
+
identifier: string;
|
|
38
|
+
name: string;
|
|
39
|
+
description: string;
|
|
40
|
+
types: ExtensionTemplateType[];
|
|
41
|
+
}
|
|
42
|
+
export interface Extension {
|
|
43
|
+
id?: string;
|
|
44
|
+
metadata?: ExtensionMetadata;
|
|
45
|
+
root: string;
|
|
46
|
+
config: ExtensionConfig;
|
|
47
|
+
}
|
|
48
|
+
export interface App {
|
|
49
|
+
root: string;
|
|
50
|
+
config: AppConfig;
|
|
51
|
+
extensions: Extension[];
|
|
52
|
+
}
|
|
53
|
+
export interface ExtensionFileDescriptor {
|
|
54
|
+
id: string;
|
|
55
|
+
type: string;
|
|
56
|
+
name: string;
|
|
57
|
+
file_name: string;
|
|
58
|
+
size: number;
|
|
59
|
+
hash: string;
|
|
60
|
+
}
|
|
61
|
+
export interface ExtensionMetadata {
|
|
62
|
+
[key: string]: ExtensionFileDescriptor[];
|
|
63
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Path, Filesystem } from '@youcan/cli-kit';
|
|
2
|
+
import { APP_CONFIG_FILENAME, DEFAULT_EXTENSIONS_DIR, EXTENSION_CONFIG_FILENAME } from '../constants.js';
|
|
3
|
+
|
|
4
|
+
async function load() {
|
|
5
|
+
const path = Path.resolve(Path.cwd(), APP_CONFIG_FILENAME);
|
|
6
|
+
if (!await Filesystem.exists) {
|
|
7
|
+
throw new Error(`app config not found at ${path}`);
|
|
8
|
+
}
|
|
9
|
+
const config = await Filesystem.readJsonFile(path);
|
|
10
|
+
const app = {
|
|
11
|
+
config,
|
|
12
|
+
extensions: [],
|
|
13
|
+
root: Path.cwd(),
|
|
14
|
+
};
|
|
15
|
+
const pattern = Path.join(app.root, `${DEFAULT_EXTENSIONS_DIR}/*`, EXTENSION_CONFIG_FILENAME);
|
|
16
|
+
const paths = await Filesystem.glob(pattern);
|
|
17
|
+
const promises = paths.map(async (p) => {
|
|
18
|
+
return {
|
|
19
|
+
root: Path.dirname(p),
|
|
20
|
+
config: await Filesystem.readJsonFile(p),
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
app.extensions = await Promise.all(promises);
|
|
24
|
+
return app;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { load };
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@youcan/app",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "1.1.0-beta.2",
|
|
5
|
+
"description": "OCLIF plugin for building apps",
|
|
6
|
+
"author": "YouCan <contact@youcan.shop> (https://youcan.shop)",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"youcan",
|
|
10
|
+
"youcan-cli",
|
|
11
|
+
"youcan-app"
|
|
12
|
+
],
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"oclif.manifest.json"
|
|
16
|
+
],
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@oclif/core": "^2.15.0",
|
|
19
|
+
"@youcan/cli-kit": "1.1.0-beta.2"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@oclif/plugin-legacy": "^1.3.0",
|
|
23
|
+
"@types/node": "^18.18.0",
|
|
24
|
+
"shx": "^0.3.4"
|
|
25
|
+
},
|
|
26
|
+
"oclif": {
|
|
27
|
+
"commands": "./dist/cli/commands"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "shx rm -rf dist && tsc --noEmit && rollup --config rollup.config.js",
|
|
31
|
+
"dev": "shx rm -rf dist && rollup --config rollup.config.js --watch",
|
|
32
|
+
"release": "pnpm publish --access public",
|
|
33
|
+
"type-check": "tsc --noEmit"
|
|
34
|
+
}
|
|
35
|
+
}
|