@wc-toolkit/cem-generator-cli 0.1.1 → 0.1.3
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/README.md +43 -7
- package/dist/cli.js +283 -28
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ pnpm add -w @wc-toolkit/cem-generator-cli
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
# Create a config and choose
|
|
14
|
+
# Create a config and choose parser plugins and integrations interactively
|
|
15
15
|
cem init
|
|
16
16
|
|
|
17
17
|
# Create a config with selected plugins without prompting
|
|
@@ -21,7 +21,7 @@ cem init --plugin lit svelte
|
|
|
21
21
|
cem generate
|
|
22
22
|
|
|
23
23
|
# Specify custom tsconfig and output path
|
|
24
|
-
cem generate --
|
|
24
|
+
cem generate --tsconfig ./tsconfig.json --output ./custom-elements.json
|
|
25
25
|
|
|
26
26
|
# Include/exclude specific files
|
|
27
27
|
cem generate --include "src/components/**" --exclude "**/*.test.ts"
|
|
@@ -35,22 +35,58 @@ cem generate --no-inheritance
|
|
|
35
35
|
|
|
36
36
|
## Options
|
|
37
37
|
|
|
38
|
-
`cem init` writes `cem-generator.config.mjs
|
|
38
|
+
`cem init` writes `cem-generator.config.mjs`. In interactive mode it first asks
|
|
39
|
+
which parser plugins to use, then presents a multi-select list of integrations:
|
|
40
|
+
`react-wrappers`, `jsx-types`, `vuejs-types`, and `svelte-types`. The type
|
|
41
|
+
integrations use `./types` and strongly typed events by default. The command
|
|
42
|
+
also asks whether to add the generated manifest to `package.json` as the
|
|
43
|
+
`customElements` property. It supports `--plugin <names...>`,
|
|
39
44
|
`--yes`, `--force`, and `--config <path>`.
|
|
40
45
|
|
|
46
|
+
When accepted, the package.json property uses the default manifest path:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"customElements": "custom-elements.json"
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Use `filePath` in the config when the manifest belongs in a build directory.
|
|
55
|
+
|
|
56
|
+
`cem generate` uses source-oriented include/exclude defaults when those flags
|
|
57
|
+
are omitted. Supplying either flag replaces its defaults. The TypeScript
|
|
58
|
+
configuration still controls which files enter the program.
|
|
59
|
+
|
|
60
|
+
The config file may set `filePath` to choose the generated manifest path. The
|
|
61
|
+
`--output` flag takes precedence when supplied.
|
|
62
|
+
|
|
63
|
+
When `cem init` installs dependencies, CLI mode installs
|
|
64
|
+
`@wc-toolkit/cem-generator-cli`, while code mode installs
|
|
65
|
+
`@wc-toolkit/cem-generator` and `tsx`. Selected parser and integration
|
|
66
|
+
packages are installed alongside the workflow package. The command prints the
|
|
67
|
+
`package.json` script to add and the package-manager command to run it.
|
|
68
|
+
|
|
69
|
+
In an interactive terminal, use the arrow keys to move through lists, Space to
|
|
70
|
+
toggle checkbox selections, and Enter to confirm. Non-interactive input falls
|
|
71
|
+
back to comma-separated selection numbers.
|
|
72
|
+
|
|
41
73
|
| Option | Description | Default |
|
|
42
74
|
|--------|-------------|---------|
|
|
43
|
-
|
|
|
75
|
+
| `--tsconfig <path>` | Path to tsconfig.json | `./tsconfig.json` |
|
|
76
|
+
| `-c, --config <path>` | Path to the generator config file | auto-detected |
|
|
44
77
|
| `-o, --output <path>` | Output file path | `./custom-elements.json` |
|
|
45
|
-
| `--include <patterns...>` | Glob patterns to include |
|
|
46
|
-
| `--exclude <patterns...>` | Glob patterns to exclude |
|
|
78
|
+
| `--include <patterns...>` | Glob patterns to include | `src/**/*.{ts,tsx,js,jsx}` when `src/` exists |
|
|
79
|
+
| `--exclude <patterns...>` | Glob patterns to exclude | Tests, specs, stories, `dist/`, and `node_modules/` |
|
|
47
80
|
| `--no-inheritance` | Disable inheritance materialization | - |
|
|
48
81
|
| `--plugin <paths...>` | Additional plugin paths to load | - |
|
|
49
82
|
| `--conflict-policy <policy>` | Detector conflict policy: `throw` \| `last-wins` | `last-wins` |
|
|
50
83
|
|
|
84
|
+
The config file may set `filePath` for the manifest path. The `--output` flag
|
|
85
|
+
takes precedence over `filePath`.
|
|
86
|
+
|
|
51
87
|
## Example
|
|
52
88
|
|
|
53
89
|
```bash
|
|
54
90
|
# Basic manifest generation
|
|
55
|
-
cem generate --
|
|
91
|
+
cem generate --tsconfig tsconfig.json --output custom-elements.json
|
|
56
92
|
```
|
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import fs from "node:fs";
|
|
|
6
6
|
import readline from "node:readline/promises";
|
|
7
7
|
import { stdin as input, stdout as output } from "node:process";
|
|
8
8
|
import { spawnSync } from "node:child_process";
|
|
9
|
+
import inquirer from "inquirer";
|
|
9
10
|
import { generateCem, loadConfig, mergeConfig } from "@wc-toolkit/cem-generator";
|
|
10
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
11
12
|
const __dirname = path.dirname(__filename);
|
|
@@ -18,6 +19,41 @@ const PLUGIN_CHOICES = [
|
|
|
18
19
|
{ name: "solid", packageName: "@wc-toolkit/cem-generator-solid", factory: "solidPlugin" },
|
|
19
20
|
{ name: "svelte", packageName: "@wc-toolkit/cem-generator-svelte", factory: "sveltePlugin" },
|
|
20
21
|
];
|
|
22
|
+
const INTEGRATION_CHOICES = [
|
|
23
|
+
{
|
|
24
|
+
name: "react-wrappers",
|
|
25
|
+
packageName: "@wc-toolkit/react-wrappers",
|
|
26
|
+
factory: "reactWrapperGeneratorPlugin",
|
|
27
|
+
options: { stronglyTypedEvents: true },
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "jsx-types",
|
|
31
|
+
packageName: "@wc-toolkit/jsx-types",
|
|
32
|
+
factory: "jsxTypesGeneratorPlugin",
|
|
33
|
+
options: { outdir: "./types", stronglyTypedEvents: true },
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "vuejs-types",
|
|
37
|
+
packageName: "@wc-toolkit/vuejs-types",
|
|
38
|
+
factory: "vuejsTypesGeneratorPlugin",
|
|
39
|
+
options: { outdir: "./types", stronglyTypedEvents: true },
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: "svelte-types",
|
|
43
|
+
packageName: "@wc-toolkit/svelte-types",
|
|
44
|
+
factory: "svelteTypesGeneratorPlugin",
|
|
45
|
+
options: { outdir: "./types", stronglyTypedEvents: true },
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
const DEFAULT_SOURCE_INCLUDE = ["src/**/*.{ts,tsx,js,jsx}"];
|
|
49
|
+
const DEFAULT_MANIFEST_PATH = "./custom-elements.json";
|
|
50
|
+
const DEFAULT_SOURCE_EXCLUDE = [
|
|
51
|
+
"**/*.test.*",
|
|
52
|
+
"**/*.spec.*",
|
|
53
|
+
"**/*.stories.*",
|
|
54
|
+
"**/dist/**",
|
|
55
|
+
"**/node_modules/**",
|
|
56
|
+
];
|
|
21
57
|
const program = new Command();
|
|
22
58
|
program
|
|
23
59
|
.name("cem")
|
|
@@ -28,7 +64,7 @@ program
|
|
|
28
64
|
.description("Generate a Custom Elements Manifest")
|
|
29
65
|
.option("--tsconfig <path>", "Path to tsconfig.json", "./tsconfig.json")
|
|
30
66
|
.option("-c, --config <path>", "Path to cem-generator config file (auto-detected if omitted)")
|
|
31
|
-
.option("-o, --output <path>", "Output file path"
|
|
67
|
+
.option("-o, --output <path>", "Output file path")
|
|
32
68
|
.option("--include <patterns...>", "Glob patterns to include")
|
|
33
69
|
.option("--exclude <patterns...>", "Glob patterns to exclude")
|
|
34
70
|
.option("--no-inheritance", "Disable inheritance materialization")
|
|
@@ -47,8 +83,8 @@ program
|
|
|
47
83
|
});
|
|
48
84
|
const cliOptions = {
|
|
49
85
|
tsConfigPath: path.resolve(options.tsconfig),
|
|
50
|
-
include: options.include,
|
|
51
|
-
exclude: options.exclude,
|
|
86
|
+
include: options.include ?? getDefaultInclude(cwd),
|
|
87
|
+
exclude: options.exclude ?? DEFAULT_SOURCE_EXCLUDE,
|
|
52
88
|
sort: options.sort !== false,
|
|
53
89
|
};
|
|
54
90
|
if (options.deprecatedLast) {
|
|
@@ -92,7 +128,7 @@ program
|
|
|
92
128
|
console.log(`Using config file: ${configPath}`);
|
|
93
129
|
}
|
|
94
130
|
const manifest = generateCem(mergedOptions);
|
|
95
|
-
const outputPath = path.resolve(options.output);
|
|
131
|
+
const outputPath = path.resolve(options.output ?? mergedOptions.filePath ?? "./custom-elements.json");
|
|
96
132
|
const outputDir = path.dirname(outputPath);
|
|
97
133
|
if (!fs.existsSync(outputDir)) {
|
|
98
134
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
@@ -111,28 +147,47 @@ program
|
|
|
111
147
|
.option("-c, --config <path>", "Config file path", "./cem-generator.config.mjs")
|
|
112
148
|
.option("--mode <mode>", "How to run the generator: cli | code")
|
|
113
149
|
.option("--output <path>", "Generated code file path", "./generate-cem.ts")
|
|
114
|
-
.option("--plugin <names...>", "
|
|
150
|
+
.option("--plugin <names...>", "Parser plugins to include (lit, fast, stencil, preact, vue, solid, svelte)")
|
|
115
151
|
.option("--install", "Install selected plugin packages")
|
|
116
152
|
.option("--yes", "Create a config without prompting for plugins")
|
|
117
153
|
.option("--force", "Overwrite existing output files")
|
|
118
154
|
.action(async (options) => {
|
|
119
155
|
try {
|
|
156
|
+
const cwd = process.cwd();
|
|
120
157
|
let mode;
|
|
121
158
|
let selectedNames;
|
|
159
|
+
let selectedIntegrationNames;
|
|
122
160
|
if (options.yes) {
|
|
123
161
|
mode = options.mode ? validateInitMode(options.mode) : "cli";
|
|
124
162
|
selectedNames = [];
|
|
163
|
+
selectedIntegrationNames = [];
|
|
125
164
|
}
|
|
126
165
|
else if (options.mode) {
|
|
127
166
|
mode = validateInitMode(options.mode);
|
|
128
|
-
|
|
167
|
+
if (options.plugin) {
|
|
168
|
+
selectedNames = validatePluginNames(options.plugin);
|
|
169
|
+
selectedIntegrationNames = [];
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
({ selectedNames, selectedIntegrationNames } = await promptForPluginsAndIntegrations());
|
|
173
|
+
}
|
|
129
174
|
}
|
|
130
175
|
else {
|
|
131
|
-
({ mode, selectedNames } = options.plugin
|
|
176
|
+
({ mode, selectedNames, selectedIntegrationNames } = options.plugin
|
|
132
177
|
? await promptForModeAndPlugins(options.plugin)
|
|
133
178
|
: await promptForModeAndPlugins());
|
|
134
179
|
}
|
|
135
180
|
const selectedPlugins = PLUGIN_CHOICES.filter((plugin) => selectedNames.includes(plugin.name));
|
|
181
|
+
const selectedIntegrations = INTEGRATION_CHOICES.filter((integration) => selectedIntegrationNames.includes(integration.name));
|
|
182
|
+
const generatorPackage = mode === "cli"
|
|
183
|
+
? "@wc-toolkit/cem-generator-cli"
|
|
184
|
+
: "@wc-toolkit/cem-generator";
|
|
185
|
+
const packagesToInstall = [
|
|
186
|
+
generatorPackage,
|
|
187
|
+
...(mode === "code" ? ["tsx"] : []),
|
|
188
|
+
...selectedPlugins.map((plugin) => plugin.packageName),
|
|
189
|
+
...selectedIntegrations.map((integration) => integration.packageName),
|
|
190
|
+
];
|
|
136
191
|
const configPath = path.resolve(options.config);
|
|
137
192
|
const outputPath = path.resolve(options.output);
|
|
138
193
|
const targetPaths = mode === "cli" ? [configPath] : [configPath, outputPath];
|
|
@@ -140,18 +195,23 @@ program
|
|
|
140
195
|
if (existingPath && !options.force) {
|
|
141
196
|
throw new Error(`Output file already exists: ${existingPath}. Use --force to overwrite it.`);
|
|
142
197
|
}
|
|
143
|
-
const shouldInstall = options.install ?? (!options.yes && !options.mode
|
|
198
|
+
const shouldInstall = options.install ?? (!options.yes && !options.mode
|
|
144
199
|
? await promptForInstall()
|
|
145
200
|
: false);
|
|
146
|
-
if (shouldInstall
|
|
147
|
-
installPluginDependencies(
|
|
201
|
+
if (shouldInstall) {
|
|
202
|
+
installPluginDependencies(packagesToInstall);
|
|
148
203
|
}
|
|
149
|
-
|
|
204
|
+
const addManifestToPackageJson = !options.yes && await promptForPackageManifest(cwd);
|
|
205
|
+
fs.writeFileSync(configPath, createConfigSource(selectedPlugins, selectedIntegrations, getDefaultInclude(cwd), DEFAULT_SOURCE_EXCLUDE), "utf-8");
|
|
150
206
|
console.log(`Created config file at ${configPath}`);
|
|
151
207
|
if (mode === "code") {
|
|
152
208
|
fs.writeFileSync(outputPath, createCodeSource(configPath, outputPath), "utf-8");
|
|
153
209
|
console.log(`Created code file at ${outputPath}`);
|
|
154
210
|
}
|
|
211
|
+
if (addManifestToPackageJson) {
|
|
212
|
+
updatePackageJsonManifestPath(cwd, DEFAULT_MANIFEST_PATH);
|
|
213
|
+
}
|
|
214
|
+
printNextSteps(mode, configPath, outputPath);
|
|
155
215
|
}
|
|
156
216
|
catch (error) {
|
|
157
217
|
console.error("Error initializing project:", error instanceof Error ? error.message : error);
|
|
@@ -164,6 +224,9 @@ function validateInitMode(mode) {
|
|
|
164
224
|
}
|
|
165
225
|
return mode;
|
|
166
226
|
}
|
|
227
|
+
function getDefaultInclude(cwd) {
|
|
228
|
+
return fs.existsSync(path.join(cwd, "src")) ? DEFAULT_SOURCE_INCLUDE : undefined;
|
|
229
|
+
}
|
|
167
230
|
async function promptForMode(prompt) {
|
|
168
231
|
const answer = await prompt.question("How will you run the generator? (1) CLI (2) code: ");
|
|
169
232
|
if (answer.trim() === "1" || answer.trim().toLowerCase() === "cli")
|
|
@@ -174,7 +237,14 @@ async function promptForMode(prompt) {
|
|
|
174
237
|
}
|
|
175
238
|
async function promptForModeAndPlugins(pluginNames) {
|
|
176
239
|
if (pluginNames) {
|
|
177
|
-
return {
|
|
240
|
+
return {
|
|
241
|
+
mode: await promptForModeWithInput(),
|
|
242
|
+
selectedNames: validatePluginNames(pluginNames),
|
|
243
|
+
selectedIntegrationNames: [],
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
if (isInteractiveTerminal()) {
|
|
247
|
+
return promptForInteractiveSelections();
|
|
178
248
|
}
|
|
179
249
|
const prompt = readline.createInterface({ input, output });
|
|
180
250
|
const lines = prompt[Symbol.asyncIterator]();
|
|
@@ -191,13 +261,37 @@ async function promptForModeAndPlugins(pluginNames) {
|
|
|
191
261
|
const pluginsLine = await lines.next();
|
|
192
262
|
if (pluginsLine.done)
|
|
193
263
|
throw new Error("No plugin selection provided.");
|
|
194
|
-
|
|
264
|
+
const selectedNames = parsePluginSelection(pluginsLine.value);
|
|
265
|
+
console.log("Which project integrations should be included? (multi-select)");
|
|
266
|
+
INTEGRATION_CHOICES.forEach((integration, index) => console.log(` ${index + 1}. ${integration.name}`));
|
|
267
|
+
console.log("Enter numbers separated by commas, or press Enter for no integrations.");
|
|
268
|
+
output.write("Integrations: ");
|
|
269
|
+
const integrationsLine = await lines.next();
|
|
270
|
+
if (integrationsLine.done)
|
|
271
|
+
throw new Error("No integration selection provided.");
|
|
272
|
+
return {
|
|
273
|
+
mode,
|
|
274
|
+
selectedNames,
|
|
275
|
+
selectedIntegrationNames: parseIntegrationSelection(integrationsLine.value),
|
|
276
|
+
};
|
|
195
277
|
}
|
|
196
278
|
finally {
|
|
197
279
|
prompt.close();
|
|
198
280
|
}
|
|
199
281
|
}
|
|
200
282
|
async function promptForModeWithInput() {
|
|
283
|
+
if (isInteractiveTerminal()) {
|
|
284
|
+
const { mode } = await inquirer.prompt({
|
|
285
|
+
type: "select",
|
|
286
|
+
name: "mode",
|
|
287
|
+
message: "How will you run the generator?",
|
|
288
|
+
choices: [
|
|
289
|
+
{ name: "CLI", value: "cli" },
|
|
290
|
+
{ name: "Code", value: "code" },
|
|
291
|
+
],
|
|
292
|
+
});
|
|
293
|
+
return mode;
|
|
294
|
+
}
|
|
201
295
|
const prompt = readline.createInterface({ input, output });
|
|
202
296
|
try {
|
|
203
297
|
return await promptForMode(prompt);
|
|
@@ -207,6 +301,15 @@ async function promptForModeWithInput() {
|
|
|
207
301
|
}
|
|
208
302
|
}
|
|
209
303
|
async function promptForInstall() {
|
|
304
|
+
if (isInteractiveTerminal()) {
|
|
305
|
+
const { install } = await inquirer.prompt({
|
|
306
|
+
type: "confirm",
|
|
307
|
+
name: "install",
|
|
308
|
+
message: "Install selected plugin packages now?",
|
|
309
|
+
default: true,
|
|
310
|
+
});
|
|
311
|
+
return install;
|
|
312
|
+
}
|
|
210
313
|
const prompt = readline.createInterface({ input, output });
|
|
211
314
|
try {
|
|
212
315
|
const answer = await prompt.question("Install selected plugin packages now? (Y/n): ");
|
|
@@ -216,6 +319,35 @@ async function promptForInstall() {
|
|
|
216
319
|
prompt.close();
|
|
217
320
|
}
|
|
218
321
|
}
|
|
322
|
+
async function promptForPackageManifest(cwd) {
|
|
323
|
+
const packageJsonPath = path.join(cwd, "package.json");
|
|
324
|
+
if (!fs.existsSync(packageJsonPath))
|
|
325
|
+
return false;
|
|
326
|
+
if (isInteractiveTerminal()) {
|
|
327
|
+
const { addManifest } = await inquirer.prompt({
|
|
328
|
+
type: "confirm",
|
|
329
|
+
name: "addManifest",
|
|
330
|
+
message: `Add \"customElements\": \"${DEFAULT_MANIFEST_PATH.slice(2)}\" to package.json?`,
|
|
331
|
+
default: false,
|
|
332
|
+
});
|
|
333
|
+
return addManifest;
|
|
334
|
+
}
|
|
335
|
+
const prompt = readline.createInterface({ input, output });
|
|
336
|
+
try {
|
|
337
|
+
const answer = await prompt.question(`Add "customElements": "${DEFAULT_MANIFEST_PATH.slice(2)}" to package.json? (y/N): `);
|
|
338
|
+
return /^y(es)?$/i.test(answer.trim());
|
|
339
|
+
}
|
|
340
|
+
finally {
|
|
341
|
+
prompt.close();
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function updatePackageJsonManifestPath(cwd, manifestPath) {
|
|
345
|
+
const packageJsonPath = path.join(cwd, "package.json");
|
|
346
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
347
|
+
packageJson.customElements = manifestPath.replace(/^\.\//, "");
|
|
348
|
+
fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, "utf-8");
|
|
349
|
+
console.log(`Updated package.json with customElements: ${packageJson.customElements}`);
|
|
350
|
+
}
|
|
219
351
|
function installPluginDependencies(packages) {
|
|
220
352
|
const packageManager = detectPackageManager();
|
|
221
353
|
const args = packageManager === "npm"
|
|
@@ -231,6 +363,35 @@ function installPluginDependencies(packages) {
|
|
|
231
363
|
throw new Error(`Failed to install plugin packages with ${packageManager}.`);
|
|
232
364
|
}
|
|
233
365
|
}
|
|
366
|
+
function printNextSteps(mode, configPath, outputPath) {
|
|
367
|
+
console.log("\nNext steps:");
|
|
368
|
+
const packageManager = detectPackageManager();
|
|
369
|
+
if (mode === "cli") {
|
|
370
|
+
const defaultConfigPath = path.resolve(process.cwd(), "cem-generator.config.mjs");
|
|
371
|
+
const configArgument = configPath === defaultConfigPath ? "" : ` --config ${toCommandPath(configPath)}`;
|
|
372
|
+
printPackageScript(`cem generate${configArgument}`, packageManager);
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
printPackageScript(`tsx ${toCommandPath(outputPath)}`, packageManager);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
function printPackageScript(command, packageManager) {
|
|
379
|
+
const runCommand = packageManager === "npm"
|
|
380
|
+
? "npm run cem"
|
|
381
|
+
: packageManager === "yarn"
|
|
382
|
+
? "yarn cem"
|
|
383
|
+
: packageManager === "bun"
|
|
384
|
+
? "bun run cem"
|
|
385
|
+
: "pnpm run cem";
|
|
386
|
+
console.log(" Add this script to package.json:");
|
|
387
|
+
console.log(` \"cem\": \"${command}\"`);
|
|
388
|
+
console.log(` Run it with: ${runCommand}`);
|
|
389
|
+
}
|
|
390
|
+
function toCommandPath(filePath) {
|
|
391
|
+
const relativePath = path.relative(process.cwd(), filePath).replaceAll(path.sep, "/");
|
|
392
|
+
const normalizedPath = relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
|
|
393
|
+
return /\s/.test(normalizedPath) ? JSON.stringify(normalizedPath) : normalizedPath;
|
|
394
|
+
}
|
|
234
395
|
function detectPackageManager() {
|
|
235
396
|
if (fs.existsSync(path.resolve("pnpm-lock.yaml")))
|
|
236
397
|
return "pnpm";
|
|
@@ -248,41 +409,135 @@ function validatePluginNames(names) {
|
|
|
248
409
|
}
|
|
249
410
|
return [...new Set(names)];
|
|
250
411
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
412
|
+
function validateIntegrationNames(names) {
|
|
413
|
+
const validNames = new Set(INTEGRATION_CHOICES.map((integration) => integration.name));
|
|
414
|
+
const invalidNames = names.filter((name) => !validNames.has(name));
|
|
415
|
+
if (invalidNames.length > 0) {
|
|
416
|
+
throw new Error(`Unknown integration(s): ${invalidNames.join(", ")}. Choose from: ${[...validNames].join(", ")}`);
|
|
417
|
+
}
|
|
418
|
+
return [...new Set(names)];
|
|
419
|
+
}
|
|
420
|
+
async function promptForPluginsAndIntegrations() {
|
|
421
|
+
if (isInteractiveTerminal()) {
|
|
422
|
+
const { plugin, integrations } = await inquirer.prompt([
|
|
423
|
+
{
|
|
424
|
+
type: "select",
|
|
425
|
+
name: "plugin",
|
|
426
|
+
message: "Which framework plugins should be included?",
|
|
427
|
+
choices: PLUGIN_CHOICES.map((plugin) => ({ name: plugin.name, value: plugin.name })),
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
type: "checkbox",
|
|
431
|
+
name: "integrations",
|
|
432
|
+
message: "Which project integrations should be included?",
|
|
433
|
+
choices: INTEGRATION_CHOICES.map((integration) => ({
|
|
434
|
+
name: integration.name,
|
|
435
|
+
value: integration.name,
|
|
436
|
+
})),
|
|
437
|
+
},
|
|
438
|
+
]);
|
|
439
|
+
return { selectedNames: [plugin], selectedIntegrationNames: integrations };
|
|
440
|
+
}
|
|
441
|
+
const prompt = readline.createInterface({ input, output });
|
|
442
|
+
const lines = prompt[Symbol.asyncIterator]();
|
|
443
|
+
try {
|
|
444
|
+
console.log("Which framework plugins should be included?");
|
|
445
|
+
PLUGIN_CHOICES.forEach((plugin, index) => console.log(` ${index + 1}. ${plugin.name}`));
|
|
446
|
+
console.log("Enter numbers separated by commas, or press Enter for vanilla only.");
|
|
447
|
+
output.write("Plugins: ");
|
|
448
|
+
const pluginsLine = await lines.next();
|
|
449
|
+
if (pluginsLine.done)
|
|
450
|
+
throw new Error("No plugin selection provided.");
|
|
451
|
+
console.log("Which project integrations should be included? (multi-select)");
|
|
452
|
+
INTEGRATION_CHOICES.forEach((integration, index) => console.log(` ${index + 1}. ${integration.name}`));
|
|
453
|
+
console.log("Enter numbers separated by commas, or press Enter for no integrations.");
|
|
454
|
+
output.write("Integrations: ");
|
|
455
|
+
const integrationsLine = await lines.next();
|
|
456
|
+
if (integrationsLine.done)
|
|
457
|
+
throw new Error("No integration selection provided.");
|
|
458
|
+
return {
|
|
459
|
+
selectedNames: parsePluginSelection(pluginsLine.value),
|
|
460
|
+
selectedIntegrationNames: parseIntegrationSelection(integrationsLine.value),
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
finally {
|
|
464
|
+
prompt.close();
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
async function promptForInteractiveSelections() {
|
|
468
|
+
const { mode, plugin, integrations } = await inquirer.prompt([
|
|
469
|
+
{
|
|
470
|
+
type: "select",
|
|
471
|
+
name: "mode",
|
|
472
|
+
message: "How will you run the generator?",
|
|
473
|
+
choices: [
|
|
474
|
+
{ name: "CLI", value: "cli" },
|
|
475
|
+
{ name: "Code", value: "code" },
|
|
476
|
+
],
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
type: "select",
|
|
480
|
+
name: "plugin",
|
|
481
|
+
message: "Which framework plugins should be included?",
|
|
482
|
+
choices: PLUGIN_CHOICES.map((plugin) => ({ name: plugin.name, value: plugin.name })),
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
type: "checkbox",
|
|
486
|
+
name: "integrations",
|
|
487
|
+
message: "Which project integrations should be included?",
|
|
488
|
+
choices: INTEGRATION_CHOICES.map((integration) => ({
|
|
489
|
+
name: integration.name,
|
|
490
|
+
value: integration.name,
|
|
491
|
+
})),
|
|
492
|
+
},
|
|
493
|
+
]);
|
|
494
|
+
return { mode, selectedNames: [plugin], selectedIntegrationNames: integrations };
|
|
495
|
+
}
|
|
496
|
+
function isInteractiveTerminal() {
|
|
497
|
+
return Boolean(input.isTTY && output.isTTY);
|
|
259
498
|
}
|
|
260
499
|
function parsePluginSelection(answer) {
|
|
500
|
+
return parseSelection(answer, PLUGIN_CHOICES, "plugin");
|
|
501
|
+
}
|
|
502
|
+
function parseIntegrationSelection(answer) {
|
|
503
|
+
return validateIntegrationNames(parseSelection(answer, INTEGRATION_CHOICES, "integration"));
|
|
504
|
+
}
|
|
505
|
+
function parseSelection(answer, choices, label) {
|
|
261
506
|
const values = answer
|
|
262
507
|
.split(",")
|
|
263
508
|
.map((value) => value.trim())
|
|
264
509
|
.filter(Boolean);
|
|
265
510
|
const names = values.map((value) => {
|
|
266
511
|
const index = Number(value) - 1;
|
|
267
|
-
if (!Number.isInteger(index) || index < 0 || index >=
|
|
268
|
-
throw new Error(`Invalid
|
|
512
|
+
if (!Number.isInteger(index) || index < 0 || index >= choices.length) {
|
|
513
|
+
throw new Error(`Invalid ${label} selection: ${value}`);
|
|
269
514
|
}
|
|
270
|
-
return
|
|
515
|
+
return choices[index].name;
|
|
271
516
|
});
|
|
272
|
-
return
|
|
517
|
+
return names;
|
|
273
518
|
}
|
|
274
|
-
function createConfigSource(plugins) {
|
|
275
|
-
const
|
|
276
|
-
const
|
|
519
|
+
function createConfigSource(plugins, integrations = [], include, exclude = DEFAULT_SOURCE_EXCLUDE) {
|
|
520
|
+
const choices = [...plugins, ...integrations];
|
|
521
|
+
const imports = choices.map((plugin) => `import { ${plugin.factory} } from "${plugin.packageName}";`);
|
|
522
|
+
const factories = choices.map((plugin) => ` ${plugin.factory}(${getPluginOptionsSource(plugin)}),`);
|
|
277
523
|
return [
|
|
278
524
|
...imports,
|
|
279
525
|
imports.length ? "" : undefined,
|
|
280
526
|
"export default {",
|
|
527
|
+
...(include ? [` include: ${JSON.stringify(include)},`] : []),
|
|
528
|
+
` exclude: ${JSON.stringify(exclude)},`,
|
|
281
529
|
...(factories.length ? [" plugins: [", ...factories, " ],"] : []),
|
|
282
530
|
"};",
|
|
283
531
|
"",
|
|
284
532
|
].filter((line) => line !== undefined).join("\n");
|
|
285
533
|
}
|
|
534
|
+
function getPluginOptionsSource(plugin) {
|
|
535
|
+
if (!("options" in plugin) || !plugin.options)
|
|
536
|
+
return "";
|
|
537
|
+
return `{ ${Object.entries(plugin.options)
|
|
538
|
+
.map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
|
|
539
|
+
.join(", ")} }`;
|
|
540
|
+
}
|
|
286
541
|
function createCodeSource(configPath, outputPath) {
|
|
287
542
|
let configImport = path.relative(path.dirname(outputPath), configPath).replaceAll(path.sep, "/");
|
|
288
543
|
if (!configImport.startsWith("."))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wc-toolkit/cem-generator-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "CLI for generating Custom Elements Manifests.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -34,8 +34,9 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"commander": "^12.1.0",
|
|
36
36
|
"custom-elements-manifest": "^2.1.0",
|
|
37
|
-
"
|
|
38
|
-
"@wc-toolkit/cem-generator
|
|
37
|
+
"inquirer": "^14.2.2",
|
|
38
|
+
"@wc-toolkit/cem-generator": "0.1.3",
|
|
39
|
+
"@wc-toolkit/cem-generator-utils": "0.1.1"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
42
|
"@types/node": "^26.4.1",
|
|
@@ -45,6 +46,6 @@
|
|
|
45
46
|
"build": "tsc",
|
|
46
47
|
"test": "node --test tests/*.test.mjs",
|
|
47
48
|
"dev": "tsx src/cli.ts",
|
|
48
|
-
"demo": "node dist/cli.js generate --
|
|
49
|
+
"demo": "node dist/cli.js generate --tsconfig ../../examples/tsconfig.json --output ../../examples/custom-elements-demo.json --conflict-policy last-wins"
|
|
49
50
|
}
|
|
50
51
|
}
|