@linyjs/cli 0.0.3 → 0.0.5
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/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +55 -4
- package/dist/commands/init.js.map +1 -1
- package/package.json +5 -1
- package/DESIGN.md +0 -901
- package/ZERO_CONFIG_GUIDE.md +0 -462
- package/src/commands/dev.ts +0 -251
- package/src/commands/init.ts +0 -75
- package/src/commands/pack.ts +0 -315
- package/src/index.ts +0 -84
- package/src/utils/builder.ts +0 -472
- package/tsconfig.json +0 -9
package/src/commands/init.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Init command - Create a new plugin project from template
|
|
3
|
-
*
|
|
4
|
-
* Downloads the default template from npm and sets up a new project
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import fs from 'fs';
|
|
8
|
-
import path from 'path';
|
|
9
|
-
import chalk from 'chalk';
|
|
10
|
-
import ora from 'ora';
|
|
11
|
-
import { execSync } from 'child_process';
|
|
12
|
-
|
|
13
|
-
interface InitOptions {
|
|
14
|
-
name?: string;
|
|
15
|
-
verbose: boolean;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Initialize a new plugin project
|
|
20
|
-
*/
|
|
21
|
-
export async function initPlugin(options: InitOptions) {
|
|
22
|
-
const targetDir = options.name || 'my-plugin';
|
|
23
|
-
const absolutePath = path.resolve(process.cwd(), targetDir);
|
|
24
|
-
|
|
25
|
-
console.log(chalk.blue('🚀 Ling Plugin Project Creator\n'));
|
|
26
|
-
|
|
27
|
-
// Check if directory already exists
|
|
28
|
-
if (fs.existsSync(absolutePath)) {
|
|
29
|
-
console.error(chalk.red(`Error: Directory "${targetDir}" already exists`));
|
|
30
|
-
process.exit(1);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const spinner1 = ora('Downloading template from npm...').start();
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
// Download template using npm create
|
|
37
|
-
const npmCommand = `npm create @linyjs/plugin-templates@latest ${targetDir}`;
|
|
38
|
-
|
|
39
|
-
if (options.verbose) {
|
|
40
|
-
console.log(chalk.gray('Running:'), npmCommand);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
execSync(npmCommand, {
|
|
44
|
-
stdio: options.verbose ? 'inherit' : 'pipe',
|
|
45
|
-
cwd: process.cwd()
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
spinner1.succeed('Template downloaded');
|
|
49
|
-
|
|
50
|
-
// Show next steps
|
|
51
|
-
console.log('\n' + chalk.green('✓ Project created successfully!\n'));
|
|
52
|
-
console.log(chalk.cyan('Next steps:'));
|
|
53
|
-
console.log(` 1. Navigate to your project:`);
|
|
54
|
-
console.log(` ${chalk.gray(`cd ${targetDir}`)}`);
|
|
55
|
-
console.log(` 2. Install dependencies:`);
|
|
56
|
-
console.log(` ${chalk.gray('npm install')}`);
|
|
57
|
-
console.log(` 3. Start development:`);
|
|
58
|
-
console.log(` ${chalk.gray('npm run dev')}`);
|
|
59
|
-
console.log(` 4. Pack for deployment:`);
|
|
60
|
-
console.log(` ${chalk.gray('npx @linyjs/cli pack')}\n`);
|
|
61
|
-
|
|
62
|
-
console.log(chalk.cyan('Learn more:'));
|
|
63
|
-
console.log(` - Plugin Development Guide: https://github.com/linyjs/ling/blob/main/spec/插件开发指南.md`);
|
|
64
|
-
console.log(` - CLI Documentation: https://github.com/linyjs/ling/blob/main/packages/cli/README.md\n`);
|
|
65
|
-
|
|
66
|
-
} catch (error) {
|
|
67
|
-
spinner1.fail('Failed to create project');
|
|
68
|
-
console.error(chalk.red('\nError:'), error instanceof Error ? error.message : error);
|
|
69
|
-
console.error(chalk.yellow('\nTroubleshooting:'));
|
|
70
|
-
console.error(' - Make sure you have Node.js >= 18 installed');
|
|
71
|
-
console.error(' - Check your internet connection');
|
|
72
|
-
console.error(' - Try again with --verbose flag for more details');
|
|
73
|
-
process.exit(1);
|
|
74
|
-
}
|
|
75
|
-
}
|
package/src/commands/pack.ts
DELETED
|
@@ -1,315 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pack plugin into .mpk file
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import fs from 'fs';
|
|
6
|
-
import path from 'path';
|
|
7
|
-
import archiver from 'archiver';
|
|
8
|
-
import chalk from 'chalk';
|
|
9
|
-
import ora from 'ora';
|
|
10
|
-
import { buildProject, type BuildOptions } from '../utils/builder.js';
|
|
11
|
-
|
|
12
|
-
interface PackOptions {
|
|
13
|
-
output: string;
|
|
14
|
-
name?: string;
|
|
15
|
-
version?: string | boolean;
|
|
16
|
-
build: boolean;
|
|
17
|
-
manifest?: string;
|
|
18
|
-
verbose: boolean;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export async function packPlugin(options: PackOptions) {
|
|
22
|
-
const projectRoot = process.cwd();
|
|
23
|
-
|
|
24
|
-
console.log(chalk.blue('📦 Ling Plugin Packer\n'));
|
|
25
|
-
|
|
26
|
-
// Step 1: Read package.json
|
|
27
|
-
const spinner1 = ora('Reading package.json...').start();
|
|
28
|
-
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
29
|
-
|
|
30
|
-
if (!fs.existsSync(packageJsonPath)) {
|
|
31
|
-
spinner1.fail('package.json not found');
|
|
32
|
-
console.error(chalk.red('\nPlease run this command in a plugin project directory.'));
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
37
|
-
spinner1.succeed('Package.json loaded');
|
|
38
|
-
|
|
39
|
-
if (options.verbose) {
|
|
40
|
-
console.log(chalk.gray(' Name:'), packageJson.name);
|
|
41
|
-
console.log(chalk.gray(' Version:'), packageJson.version);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Step 2: Build project (if needed)
|
|
45
|
-
if (options.build) {
|
|
46
|
-
const buildOptions: BuildOptions = {
|
|
47
|
-
projectRoot,
|
|
48
|
-
verbose: options.verbose,
|
|
49
|
-
// esbuild is used by default - no need for users to configure webpack/rollup
|
|
50
|
-
// CLI handles all build optimization automatically
|
|
51
|
-
minify: true, // Always minify for production bundles
|
|
52
|
-
external: [
|
|
53
|
-
'react',
|
|
54
|
-
'react-dom',
|
|
55
|
-
'express',
|
|
56
|
-
'@linyjs/*',
|
|
57
|
-
'@ling/*'
|
|
58
|
-
]
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const buildResult = await buildProject(buildOptions);
|
|
62
|
-
|
|
63
|
-
if (options.verbose) {
|
|
64
|
-
console.log(chalk.gray('\n📊 Build Statistics:'));
|
|
65
|
-
console.log(chalk.gray(' Duration:'), `${buildResult.duration}ms`);
|
|
66
|
-
console.log(chalk.gray(' Files:'), buildResult.files.length);
|
|
67
|
-
console.log(chalk.gray(' Size:'), formatFileSize(buildResult.size));
|
|
68
|
-
console.log(chalk.gray(' Output:'), buildResult.outputDir);
|
|
69
|
-
}
|
|
70
|
-
} else {
|
|
71
|
-
console.log(chalk.yellow('⚠ Skipping build step (--no-build)'));
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Step 3: Generate manifest.json
|
|
75
|
-
const spinner3 = ora('Generating manifest.json...').start();
|
|
76
|
-
const distDir = path.join(projectRoot, 'dist');
|
|
77
|
-
|
|
78
|
-
if (!fs.existsSync(distDir)) {
|
|
79
|
-
spinner3.fail('dist/ directory not found');
|
|
80
|
-
console.error(chalk.red('\nPlease build the project first or remove --no-build flag.'));
|
|
81
|
-
process.exit(1);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const manifest = {
|
|
85
|
-
name: options.name || packageJson.name,
|
|
86
|
-
version: options.version || packageJson.version,
|
|
87
|
-
description: packageJson.description || '',
|
|
88
|
-
author: typeof packageJson.author === 'string'
|
|
89
|
-
? packageJson.author
|
|
90
|
-
: packageJson.author?.name || '',
|
|
91
|
-
serverEntry: detectEntry(distDir, 'server'),
|
|
92
|
-
clientEntry: detectEntry(distDir, 'client'),
|
|
93
|
-
dependencies: extractDependencies(packageJson),
|
|
94
|
-
permissions: extractPermissions(packageJson)
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
// Write manifest.json to dist/
|
|
98
|
-
const manifestPath = path.join(distDir, 'manifest.json');
|
|
99
|
-
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
100
|
-
spinner3.succeed('Manifest generated');
|
|
101
|
-
|
|
102
|
-
if (options.verbose) {
|
|
103
|
-
console.log(chalk.gray(' Server entry:'), manifest.serverEntry);
|
|
104
|
-
console.log(chalk.gray(' Client entry:'), manifest.clientEntry);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Step 4: Validate structure
|
|
108
|
-
const spinner4 = ora('Validating plugin structure...').start();
|
|
109
|
-
const validationErrors = validatePluginStructure(distDir, manifest);
|
|
110
|
-
|
|
111
|
-
if (validationErrors.length > 0) {
|
|
112
|
-
spinner4.fail('Validation failed');
|
|
113
|
-
console.error(chalk.red('\nValidation errors:'));
|
|
114
|
-
validationErrors.forEach(err => {
|
|
115
|
-
console.error(chalk.red(` - ${err}`));
|
|
116
|
-
});
|
|
117
|
-
process.exit(1);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
spinner4.succeed('Structure validated');
|
|
121
|
-
|
|
122
|
-
// Step 5: Pack to .mpk
|
|
123
|
-
const outputDir = path.resolve(options.output);
|
|
124
|
-
const outputFile = path.join(outputDir, `${manifest.name}-${manifest.version}.mpk`);
|
|
125
|
-
|
|
126
|
-
// Ensure output directory exists
|
|
127
|
-
if (!fs.existsSync(outputDir)) {
|
|
128
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const spinner5 = ora('Packing plugin...').start();
|
|
132
|
-
|
|
133
|
-
try {
|
|
134
|
-
await createZipArchive(distDir, outputFile);
|
|
135
|
-
spinner5.succeed('Plugin packed successfully');
|
|
136
|
-
} catch (error) {
|
|
137
|
-
spinner5.fail('Pack failed');
|
|
138
|
-
console.error(chalk.red('\nPack error:'));
|
|
139
|
-
if (error instanceof Error) {
|
|
140
|
-
console.error(error.message);
|
|
141
|
-
}
|
|
142
|
-
process.exit(1);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Step 6: Show result
|
|
146
|
-
const stats = fs.statSync(outputFile);
|
|
147
|
-
const fileSize = formatFileSize(stats.size);
|
|
148
|
-
|
|
149
|
-
console.log('\n' + chalk.green('✓ Plugin ready!'));
|
|
150
|
-
console.log(chalk.gray(' File:'), chalk.cyan(outputFile));
|
|
151
|
-
console.log(chalk.gray(' Size:'), chalk.cyan(fileSize));
|
|
152
|
-
console.log(chalk.gray(' Version:'), chalk.cyan(manifest.version));
|
|
153
|
-
|
|
154
|
-
console.log('\n' + chalk.blue('Next steps:'));
|
|
155
|
-
console.log(' 1. Test the plugin:');
|
|
156
|
-
console.log(chalk.gray(' cp ' + outputFile + ' /path/to/ling/packages/ssr/extensions/'));
|
|
157
|
-
console.log(' 2. Restart SSR server:');
|
|
158
|
-
console.log(chalk.gray(' cd packages/ssr && npx tsx src/demo/dev-server-with-pkg.tsx'));
|
|
159
|
-
console.log(' 3. Validate the plugin:');
|
|
160
|
-
console.log(chalk.gray(' npx @linyjs/cli validate ' + outputFile));
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Detect entry file in dist directory
|
|
165
|
-
*/
|
|
166
|
-
function detectEntry(distDir: string, type: 'server' | 'client'): string {
|
|
167
|
-
const entryPath = path.join(distDir, type, 'index.js');
|
|
168
|
-
|
|
169
|
-
if (fs.existsSync(entryPath)) {
|
|
170
|
-
return `${type}/index.js`;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Try other common patterns
|
|
174
|
-
const alternatives = [
|
|
175
|
-
`${type}.js`,
|
|
176
|
-
`${type}/index.ts`,
|
|
177
|
-
`index.${type}.js`
|
|
178
|
-
];
|
|
179
|
-
|
|
180
|
-
for (const alt of alternatives) {
|
|
181
|
-
if (fs.existsSync(path.join(distDir, alt))) {
|
|
182
|
-
return alt;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
throw new Error(`Cannot find ${type} entry file in dist/`);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Extract dependencies from package.json
|
|
191
|
-
*/
|
|
192
|
-
function extractDependencies(packageJson: any): string[] {
|
|
193
|
-
const deps: string[] = [];
|
|
194
|
-
|
|
195
|
-
// Check dependencies
|
|
196
|
-
if (packageJson.dependencies) {
|
|
197
|
-
Object.keys(packageJson.dependencies).forEach(dep => {
|
|
198
|
-
if (dep.startsWith('@linyjs/') || dep.startsWith('@ling/')) {
|
|
199
|
-
deps.push(`${dep}@${packageJson.dependencies[dep]}`);
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return deps;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Extract permissions from package.json keywords or custom field
|
|
209
|
-
*/
|
|
210
|
-
function extractPermissions(packageJson: any): string[] {
|
|
211
|
-
// Check for custom permissions field
|
|
212
|
-
if (packageJson.ling?.permissions) {
|
|
213
|
-
return packageJson.ling.permissions;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Or extract from keywords
|
|
217
|
-
if (packageJson.keywords) {
|
|
218
|
-
return packageJson.keywords
|
|
219
|
-
.filter((k: string) => k.startsWith('permission:'))
|
|
220
|
-
.map((k: string) => k.replace('permission:', ''));
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
return [];
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Validate plugin structure
|
|
228
|
-
*/
|
|
229
|
-
function validatePluginStructure(distDir: string, manifest: any): string[] {
|
|
230
|
-
const errors: string[] = [];
|
|
231
|
-
|
|
232
|
-
// Check manifest.json
|
|
233
|
-
if (!fs.existsSync(path.join(distDir, 'manifest.json'))) {
|
|
234
|
-
errors.push('manifest.json is missing');
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Check server entry
|
|
238
|
-
if (manifest.serverEntry) {
|
|
239
|
-
const serverPath = path.join(distDir, manifest.serverEntry);
|
|
240
|
-
if (!fs.existsSync(serverPath)) {
|
|
241
|
-
errors.push(`Server entry not found: ${manifest.serverEntry}`);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Check client entry
|
|
246
|
-
if (manifest.clientEntry) {
|
|
247
|
-
const clientPath = path.join(distDir, manifest.clientEntry);
|
|
248
|
-
if (!fs.existsSync(clientPath)) {
|
|
249
|
-
errors.push(`Client entry not found: ${manifest.clientEntry}`);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Check for required fields
|
|
254
|
-
if (!manifest.name) {
|
|
255
|
-
errors.push('Plugin name is required');
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if (!manifest.version) {
|
|
259
|
-
errors.push('Plugin version is required');
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return errors;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Create ZIP archive
|
|
267
|
-
*/
|
|
268
|
-
function createZipArchive(sourceDir: string, outputPath: string): Promise<void> {
|
|
269
|
-
return new Promise((resolve, reject) => {
|
|
270
|
-
const output = fs.createWriteStream(outputPath);
|
|
271
|
-
const archive = archiver('zip', {
|
|
272
|
-
zlib: { level: 9 } // Maximum compression
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
output.on('close', () => {
|
|
276
|
-
resolve();
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
archive.on('error', (err: Error) => {
|
|
280
|
-
reject(err);
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
archive.pipe(output);
|
|
284
|
-
|
|
285
|
-
// Add all files from dist directory
|
|
286
|
-
archive.directory(sourceDir, false, (entry: any) => {
|
|
287
|
-
// Exclude unnecessary files
|
|
288
|
-
if (entry.name.includes('node_modules') ||
|
|
289
|
-
entry.name.includes('.git') ||
|
|
290
|
-
entry.name.endsWith('.test.js') ||
|
|
291
|
-
entry.name.endsWith('.spec.js')) {
|
|
292
|
-
return false;
|
|
293
|
-
}
|
|
294
|
-
return entry;
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
archive.finalize();
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Format file size
|
|
303
|
-
*/
|
|
304
|
-
function formatFileSize(bytes: number): string {
|
|
305
|
-
const units = ['B', 'KB', 'MB', 'GB'];
|
|
306
|
-
let size = bytes;
|
|
307
|
-
let unitIndex = 0;
|
|
308
|
-
|
|
309
|
-
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
310
|
-
size /= 1024;
|
|
311
|
-
unitIndex++;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
return `${size.toFixed(2)} ${units[unitIndex]}`;
|
|
315
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @linyjs/cli - Ling 框架命令行工具
|
|
5
|
-
*
|
|
6
|
-
* 提供插件打包、开发、发布等实用功能
|
|
7
|
-
*
|
|
8
|
-
* 使用 Cleye 作为 CLI 框架
|
|
9
|
-
* - 零配置 TypeScript
|
|
10
|
-
* - 自动类型推断
|
|
11
|
-
* - 简洁的函数式 API
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { cli } from 'cleye';
|
|
15
|
-
import { createRequire } from 'module';
|
|
16
|
-
|
|
17
|
-
const require = createRequire(import.meta.url);
|
|
18
|
-
const packageJson = require('../package.json');
|
|
19
|
-
|
|
20
|
-
// Main CLI with subcommands
|
|
21
|
-
cli(
|
|
22
|
-
{
|
|
23
|
-
name: '@linyjs/cli',
|
|
24
|
-
version: packageJson.version,
|
|
25
|
-
},
|
|
26
|
-
async (argv: any) => {
|
|
27
|
-
// Get the command from argv._
|
|
28
|
-
const command = argv._[0];
|
|
29
|
-
|
|
30
|
-
switch (command) {
|
|
31
|
-
case 'pack':
|
|
32
|
-
const { packPlugin } = await import('./commands/pack.js');
|
|
33
|
-
await packPlugin({
|
|
34
|
-
output: argv.flags.output || '.',
|
|
35
|
-
name: argv.flags.name,
|
|
36
|
-
version: argv.flags.version,
|
|
37
|
-
build: argv.flags.build !== false,
|
|
38
|
-
manifest: argv.flags.manifest,
|
|
39
|
-
verbose: argv.flags.verbose || false
|
|
40
|
-
});
|
|
41
|
-
break;
|
|
42
|
-
|
|
43
|
-
case 'dev':
|
|
44
|
-
const { devPlugin } = await import('./commands/dev.js');
|
|
45
|
-
await devPlugin({
|
|
46
|
-
target: argv.flags.target,
|
|
47
|
-
verbose: argv.flags.verbose || false
|
|
48
|
-
});
|
|
49
|
-
break;
|
|
50
|
-
|
|
51
|
-
case 'init':
|
|
52
|
-
const { initPlugin } = await import('./commands/init.js');
|
|
53
|
-
await initPlugin({
|
|
54
|
-
name: argv._[1],
|
|
55
|
-
verbose: argv.flags.verbose || false
|
|
56
|
-
});
|
|
57
|
-
break;
|
|
58
|
-
|
|
59
|
-
default:
|
|
60
|
-
// Show help if no command or unknown command
|
|
61
|
-
console.log(`
|
|
62
|
-
📦 @linyjs/cli v${packageJson.version}
|
|
63
|
-
|
|
64
|
-
Usage:
|
|
65
|
-
npx @linyjs/cli <command> [options]
|
|
66
|
-
|
|
67
|
-
Commands:
|
|
68
|
-
pack Pack a plugin project into .mpk file
|
|
69
|
-
dev Quick development workflow (pack + install + register)
|
|
70
|
-
init Create a new plugin project from template
|
|
71
|
-
|
|
72
|
-
Examples:
|
|
73
|
-
npx @linyjs/cli pack # Pack plugin
|
|
74
|
-
npx @linyjs/cli pack -o ./releases # Specify output directory
|
|
75
|
-
npx @linyjs/cli dev # Dev mode (pack + install)
|
|
76
|
-
npx @linyjs/cli dev --target /path # Specify extensions directory
|
|
77
|
-
npx @linyjs/cli init my-plugin # Create new plugin project
|
|
78
|
-
|
|
79
|
-
Run 'npx @linyjs/cli <command> --help' for more information.
|
|
80
|
-
`.trim());
|
|
81
|
-
break;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
);
|