vyriy 0.8.1 → 0.8.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/README.md +45 -0
- package/args.js +5 -1
- package/cli.js +20 -1
- package/config-cli.d.ts +1 -0
- package/config-cli.js +69 -0
- package/config-targets.d.ts +4 -0
- package/config-targets.js +82 -0
- package/file-exists.d.ts +1 -0
- package/file-exists.js +13 -0
- package/index.d.ts +3 -0
- package/index.js +3 -0
- package/package-dependencies.d.ts +1 -0
- package/package-dependencies.js +28 -0
- package/package.json +75 -5
- package/parse-config-args.d.ts +2 -0
- package/parse-config-args.js +44 -0
- package/select-configs.d.ts +2 -0
- package/select-configs.js +50 -0
- package/types.d.ts +18 -1
- package/write-config-files.d.ts +10 -0
- package/write-config-files.js +29 -0
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# vyriy
|
|
2
2
|
|
|
3
|
+
Part of [Vyriy](https://vyriy.dev) - a calm architecture toolkit for TypeScript, React, SSR, SSG, APIs, and cloud-ready apps.
|
|
4
|
+
|
|
5
|
+
Full documentation: https://vyriy.dev/docs/vyriy/
|
|
6
|
+
|
|
3
7
|
Interactive project master for Vyriy projects.
|
|
4
8
|
|
|
5
9
|
## Purpose
|
|
@@ -30,6 +34,7 @@ vyriy create . Initialise a new Vyriy project in the current directory
|
|
|
30
34
|
vyriy dist Prepare dist package metadata without publishing to npm
|
|
31
35
|
vyriy static [dir] Serve a static directory (defaults to .)
|
|
32
36
|
vyriy check Check local environment (Node.js and Yarn versions)
|
|
37
|
+
vyriy config [tool] Generate thin local config files
|
|
33
38
|
vyriy --help, -h Show help
|
|
34
39
|
vyriy --version, -v Show version
|
|
35
40
|
```
|
|
@@ -77,6 +82,46 @@ vyriy check --help
|
|
|
77
82
|
vyriy check --version
|
|
78
83
|
```
|
|
79
84
|
|
|
85
|
+
### `config`
|
|
86
|
+
|
|
87
|
+
Vyriy can generate thin local config files that connect your project to Vyriy
|
|
88
|
+
standards.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
vyriy config init
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Or configure tools one by one:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
vyriy config typescript
|
|
98
|
+
vyriy config eslint
|
|
99
|
+
vyriy config prettier
|
|
100
|
+
vyriy config jest
|
|
101
|
+
vyriy config storybook
|
|
102
|
+
vyriy config stylelint
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Generated files stay intentionally small. For TypeScript, Vyriy writes a local
|
|
106
|
+
`tsconfig.json` that extends `@vyriy/typescript-config` and includes common
|
|
107
|
+
project paths such as `.storybook`, `packages`, `workspaces`, and root
|
|
108
|
+
TypeScript files.
|
|
109
|
+
|
|
110
|
+
Existing files are skipped by default:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
vyriy config typescript --force
|
|
114
|
+
vyriy config init --dry-run
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The command does not install dependencies. If required Vyriy config packages are
|
|
118
|
+
missing from `package.json`, it prints a suggested install command:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
yarn add -D @vyriy/typescript-config @vyriy/eslint-config
|
|
122
|
+
npm install --save-dev @vyriy/typescript-config @vyriy/eslint-config
|
|
123
|
+
```
|
|
124
|
+
|
|
80
125
|
### `dist`
|
|
81
126
|
|
|
82
127
|
Prepares every package inside the `dist/` directory for npm publishing:
|
package/args.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export const parseArgs = (args) => {
|
|
2
2
|
const [command = '', ...commandArgs] = args;
|
|
3
|
-
if (command === 'check' ||
|
|
3
|
+
if (command === 'check' ||
|
|
4
|
+
command === 'config' ||
|
|
5
|
+
command === 'create' ||
|
|
6
|
+
command === 'dist' ||
|
|
7
|
+
command === 'static') {
|
|
4
8
|
return { type: command, args: commandArgs };
|
|
5
9
|
}
|
|
6
10
|
if (args.includes('--help') || args.includes('-h')) {
|
package/cli.js
CHANGED
|
@@ -8,6 +8,7 @@ Usage:
|
|
|
8
8
|
vyriy dist Prepare dist package metadata without publishing to npm
|
|
9
9
|
vyriy static [dir] Serve a static directory (defaults to .)
|
|
10
10
|
vyriy check Check local environment
|
|
11
|
+
vyriy config [tool] Generate thin local config files
|
|
11
12
|
vyriy --help, -h Show help
|
|
12
13
|
vyriy --version, -v Show version
|
|
13
14
|
|
|
@@ -26,6 +27,17 @@ Static options:
|
|
|
26
27
|
vyriy static dist --spa Enable SPA fallback mode
|
|
27
28
|
vyriy static dist --fallback index.html SPA fallback file
|
|
28
29
|
|
|
30
|
+
Config options:
|
|
31
|
+
vyriy config init Select configs to create
|
|
32
|
+
vyriy config typescript Create tsconfig.json
|
|
33
|
+
vyriy config eslint Create eslint.config.js
|
|
34
|
+
vyriy config prettier Create prettier.config.js
|
|
35
|
+
vyriy config jest Create jest.config.js
|
|
36
|
+
vyriy config storybook Create .storybook config files
|
|
37
|
+
vyriy config stylelint Create stylelint.config.js
|
|
38
|
+
vyriy config init --force Overwrite existing config files
|
|
39
|
+
vyriy config init --dry-run Print config files without writing them
|
|
40
|
+
|
|
29
41
|
Examples:
|
|
30
42
|
vyriy create app
|
|
31
43
|
vyriy create app --dry-run
|
|
@@ -37,7 +49,9 @@ Examples:
|
|
|
37
49
|
vyriy static dist --cache static
|
|
38
50
|
vyriy static dist --spa --fallback index.html --cache static
|
|
39
51
|
vyriy static dist
|
|
40
|
-
vyriy check
|
|
52
|
+
vyriy check
|
|
53
|
+
vyriy config init
|
|
54
|
+
vyriy config typescript`;
|
|
41
55
|
export const cli = async (args = []) => {
|
|
42
56
|
const command = parseArgs(args);
|
|
43
57
|
switch (command.type) {
|
|
@@ -69,6 +83,11 @@ export const cli = async (args = []) => {
|
|
|
69
83
|
await runCreateCli(command.args, 'vyriy create', false);
|
|
70
84
|
break;
|
|
71
85
|
}
|
|
86
|
+
case 'config': {
|
|
87
|
+
const { runConfigCli } = await import('./config-cli.js');
|
|
88
|
+
await runConfigCli(command.args);
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
72
91
|
default:
|
|
73
92
|
console.error(`Unknown command: ${command.command}\n`);
|
|
74
93
|
console.log(text);
|
package/config-cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const runConfigCli: (args?: readonly string[], cwd?: string) => Promise<void>;
|
package/config-cli.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { configTargets } from './config-targets.js';
|
|
2
|
+
import { fileExists } from './file-exists.js';
|
|
3
|
+
import { findMissingPackages } from './package-dependencies.js';
|
|
4
|
+
import { parseConfigArgs } from './parse-config-args.js';
|
|
5
|
+
import { selectConfigs } from './select-configs.js';
|
|
6
|
+
import { writeConfigFiles } from './write-config-files.js';
|
|
7
|
+
const helpText = `Usage:
|
|
8
|
+
vyriy config init
|
|
9
|
+
vyriy config typescript
|
|
10
|
+
vyriy config eslint
|
|
11
|
+
vyriy config prettier
|
|
12
|
+
vyriy config jest
|
|
13
|
+
vyriy config storybook
|
|
14
|
+
vyriy config stylelint
|
|
15
|
+
|
|
16
|
+
Options:
|
|
17
|
+
--force Overwrite existing config files
|
|
18
|
+
--dry-run Print files that would be created without writing them
|
|
19
|
+
--help Show config help`;
|
|
20
|
+
const unique = (values) => [...new Set(values)];
|
|
21
|
+
const collectFiles = (names) => {
|
|
22
|
+
return names.flatMap((name) => [...configTargets[name].files]);
|
|
23
|
+
};
|
|
24
|
+
const printMissingPackages = (missingPackages) => {
|
|
25
|
+
if (missingPackages.length === 0) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
console.log('');
|
|
29
|
+
console.log('Missing Vyriy config packages:');
|
|
30
|
+
console.log('');
|
|
31
|
+
for (const packageName of missingPackages) {
|
|
32
|
+
console.log(`- ${packageName}`);
|
|
33
|
+
}
|
|
34
|
+
console.log('');
|
|
35
|
+
console.log('Install them with:');
|
|
36
|
+
console.log('');
|
|
37
|
+
console.log(`yarn add -D ${missingPackages.join(' ')}`);
|
|
38
|
+
};
|
|
39
|
+
export const runConfigCli = async (args = [], cwd = process.cwd()) => {
|
|
40
|
+
const command = parseConfigArgs(args);
|
|
41
|
+
if (command.help) {
|
|
42
|
+
console.log(helpText);
|
|
43
|
+
process.exitCode = 0;
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (command.type === 'unknown') {
|
|
47
|
+
console.error('Unknown config command.');
|
|
48
|
+
console.log(helpText);
|
|
49
|
+
process.exitCode = 1;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const names = command.type === 'init' ? await selectConfigs() : command.names;
|
|
53
|
+
const files = collectFiles(names);
|
|
54
|
+
const writtenFiles = await writeConfigFiles({
|
|
55
|
+
cwd,
|
|
56
|
+
dryRun: command.dryRun,
|
|
57
|
+
exists: fileExists,
|
|
58
|
+
files,
|
|
59
|
+
force: command.force,
|
|
60
|
+
});
|
|
61
|
+
if (command.dryRun || writtenFiles.length === 0) {
|
|
62
|
+
process.exitCode = 0;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const packageNames = unique(names.map((name) => configTargets[name].packageName));
|
|
66
|
+
const missingPackages = await findMissingPackages(cwd, packageNames);
|
|
67
|
+
printMissingPackages(missingPackages);
|
|
68
|
+
process.exitCode = 0;
|
|
69
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const typescriptContent = `${JSON.stringify({
|
|
2
|
+
extends: '@vyriy/typescript-config/index.json',
|
|
3
|
+
include: [
|
|
4
|
+
'.storybook/**/*.ts',
|
|
5
|
+
'.storybook/**/*.tsx',
|
|
6
|
+
'packages/**/*.ts',
|
|
7
|
+
'packages/**/*.tsx',
|
|
8
|
+
'workspaces/**/*.ts',
|
|
9
|
+
'workspaces/**/*.tsx',
|
|
10
|
+
'*.ts',
|
|
11
|
+
'*.tsx',
|
|
12
|
+
],
|
|
13
|
+
}, null, 2)}
|
|
14
|
+
`;
|
|
15
|
+
const eslintContent = `import config from '@vyriy/eslint-config';
|
|
16
|
+
|
|
17
|
+
export default config;
|
|
18
|
+
`;
|
|
19
|
+
const prettierContent = `export { default } from '@vyriy/prettier-config';
|
|
20
|
+
`;
|
|
21
|
+
const jestContent = `export { default } from '@vyriy/jest-config';
|
|
22
|
+
`;
|
|
23
|
+
const stylelintContent = `export { default } from '@vyriy/stylelint-config';
|
|
24
|
+
`;
|
|
25
|
+
const storybookMainContent = `import config from '@vyriy/storybook-config';
|
|
26
|
+
|
|
27
|
+
import type { StorybookConfig } from '@vyriy/storybook-config';
|
|
28
|
+
|
|
29
|
+
const main: StorybookConfig = {
|
|
30
|
+
...config,
|
|
31
|
+
stories: [
|
|
32
|
+
'../**/*.mdx',
|
|
33
|
+
'../**/*.stories.@(js|jsx|mjs|ts|tsx)',
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default main;
|
|
38
|
+
`;
|
|
39
|
+
const storybookPreviewContent = `export { default } from '@vyriy/storybook-config/preview.js';
|
|
40
|
+
`;
|
|
41
|
+
export const configTargets = {
|
|
42
|
+
eslint: {
|
|
43
|
+
name: 'eslint',
|
|
44
|
+
packageName: '@vyriy/eslint-config',
|
|
45
|
+
files: [{ path: 'eslint.config.js', content: eslintContent }],
|
|
46
|
+
},
|
|
47
|
+
jest: {
|
|
48
|
+
name: 'jest',
|
|
49
|
+
packageName: '@vyriy/jest-config',
|
|
50
|
+
files: [{ path: 'jest.config.js', content: jestContent }],
|
|
51
|
+
},
|
|
52
|
+
prettier: {
|
|
53
|
+
name: 'prettier',
|
|
54
|
+
packageName: '@vyriy/prettier-config',
|
|
55
|
+
files: [{ path: 'prettier.config.js', content: prettierContent }],
|
|
56
|
+
},
|
|
57
|
+
storybook: {
|
|
58
|
+
name: 'storybook',
|
|
59
|
+
packageName: '@vyriy/storybook-config',
|
|
60
|
+
files: [
|
|
61
|
+
{ path: '.storybook/main.ts', content: storybookMainContent },
|
|
62
|
+
{ path: '.storybook/preview.ts', content: storybookPreviewContent },
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
stylelint: {
|
|
66
|
+
name: 'stylelint',
|
|
67
|
+
packageName: '@vyriy/stylelint-config',
|
|
68
|
+
files: [{ path: 'stylelint.config.js', content: stylelintContent }],
|
|
69
|
+
},
|
|
70
|
+
typescript: {
|
|
71
|
+
name: 'typescript',
|
|
72
|
+
packageName: '@vyriy/typescript-config',
|
|
73
|
+
files: [{ path: 'tsconfig.json', content: typescriptContent }],
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
export const defaultConfigNames = [
|
|
77
|
+
'typescript',
|
|
78
|
+
'eslint',
|
|
79
|
+
'prettier',
|
|
80
|
+
'jest',
|
|
81
|
+
];
|
|
82
|
+
export const allConfigNames = Object.keys(configTargets);
|
package/file-exists.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const fileExists: (path: string) => Promise<boolean>;
|
package/file-exists.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { access } from 'node:fs/promises';
|
|
2
|
+
export const fileExists = async (path) => {
|
|
3
|
+
try {
|
|
4
|
+
await access(path);
|
|
5
|
+
return true;
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
throw error;
|
|
12
|
+
}
|
|
13
|
+
};
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const findMissingPackages: (cwd: string, packageNames: readonly string[]) => Promise<string[]>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
const dependencyFields = [
|
|
4
|
+
'dependencies',
|
|
5
|
+
'devDependencies',
|
|
6
|
+
'peerDependencies',
|
|
7
|
+
'optionalDependencies',
|
|
8
|
+
];
|
|
9
|
+
const readPackageJson = async (cwd) => {
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(await readFile(join(cwd, 'package.json'), 'utf8'));
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
export const findMissingPackages = async (cwd, packageNames) => {
|
|
21
|
+
const packageJson = await readPackageJson(cwd);
|
|
22
|
+
if (!packageJson) {
|
|
23
|
+
return [...packageNames];
|
|
24
|
+
}
|
|
25
|
+
return packageNames.filter((packageName) => {
|
|
26
|
+
return !dependencyFields.some((field) => Boolean(packageJson[field]?.[packageName]));
|
|
27
|
+
});
|
|
28
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vyriy",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "Interactive project master for calm cloud-ready applications.",
|
|
5
5
|
"homepage": "https://vyriy.dev/docs/vyriy/",
|
|
6
6
|
"type": "module",
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
},
|
|
11
11
|
"packageManager": "yarn@4.16.0",
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@vyriy/check": "0.8.
|
|
14
|
-
"@vyriy/create": "0.8.
|
|
15
|
-
"@vyriy/dist": "0.8.
|
|
16
|
-
"@vyriy/static": "0.8.
|
|
13
|
+
"@vyriy/check": "0.8.2",
|
|
14
|
+
"@vyriy/create": "0.8.2",
|
|
15
|
+
"@vyriy/dist": "0.8.2",
|
|
16
|
+
"@vyriy/static": "0.8.2"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
19
|
"@testing-library/dom": "^10.4.1",
|
|
@@ -146,6 +146,36 @@
|
|
|
146
146
|
"import": "./cli.js",
|
|
147
147
|
"default": "./cli.js"
|
|
148
148
|
},
|
|
149
|
+
"./config-cli": {
|
|
150
|
+
"types": "./config-cli.d.ts",
|
|
151
|
+
"import": "./config-cli.js",
|
|
152
|
+
"default": "./config-cli.js"
|
|
153
|
+
},
|
|
154
|
+
"./config-cli.js": {
|
|
155
|
+
"types": "./config-cli.d.ts",
|
|
156
|
+
"import": "./config-cli.js",
|
|
157
|
+
"default": "./config-cli.js"
|
|
158
|
+
},
|
|
159
|
+
"./config-targets": {
|
|
160
|
+
"types": "./config-targets.d.ts",
|
|
161
|
+
"import": "./config-targets.js",
|
|
162
|
+
"default": "./config-targets.js"
|
|
163
|
+
},
|
|
164
|
+
"./config-targets.js": {
|
|
165
|
+
"types": "./config-targets.d.ts",
|
|
166
|
+
"import": "./config-targets.js",
|
|
167
|
+
"default": "./config-targets.js"
|
|
168
|
+
},
|
|
169
|
+
"./file-exists": {
|
|
170
|
+
"types": "./file-exists.d.ts",
|
|
171
|
+
"import": "./file-exists.js",
|
|
172
|
+
"default": "./file-exists.js"
|
|
173
|
+
},
|
|
174
|
+
"./file-exists.js": {
|
|
175
|
+
"types": "./file-exists.d.ts",
|
|
176
|
+
"import": "./file-exists.js",
|
|
177
|
+
"default": "./file-exists.js"
|
|
178
|
+
},
|
|
149
179
|
"./index": {
|
|
150
180
|
"types": "./index.d.ts",
|
|
151
181
|
"import": "./index.js",
|
|
@@ -155,6 +185,46 @@
|
|
|
155
185
|
"types": "./index.d.ts",
|
|
156
186
|
"import": "./index.js",
|
|
157
187
|
"default": "./index.js"
|
|
188
|
+
},
|
|
189
|
+
"./package-dependencies": {
|
|
190
|
+
"types": "./package-dependencies.d.ts",
|
|
191
|
+
"import": "./package-dependencies.js",
|
|
192
|
+
"default": "./package-dependencies.js"
|
|
193
|
+
},
|
|
194
|
+
"./package-dependencies.js": {
|
|
195
|
+
"types": "./package-dependencies.d.ts",
|
|
196
|
+
"import": "./package-dependencies.js",
|
|
197
|
+
"default": "./package-dependencies.js"
|
|
198
|
+
},
|
|
199
|
+
"./parse-config-args": {
|
|
200
|
+
"types": "./parse-config-args.d.ts",
|
|
201
|
+
"import": "./parse-config-args.js",
|
|
202
|
+
"default": "./parse-config-args.js"
|
|
203
|
+
},
|
|
204
|
+
"./parse-config-args.js": {
|
|
205
|
+
"types": "./parse-config-args.d.ts",
|
|
206
|
+
"import": "./parse-config-args.js",
|
|
207
|
+
"default": "./parse-config-args.js"
|
|
208
|
+
},
|
|
209
|
+
"./select-configs": {
|
|
210
|
+
"types": "./select-configs.d.ts",
|
|
211
|
+
"import": "./select-configs.js",
|
|
212
|
+
"default": "./select-configs.js"
|
|
213
|
+
},
|
|
214
|
+
"./select-configs.js": {
|
|
215
|
+
"types": "./select-configs.d.ts",
|
|
216
|
+
"import": "./select-configs.js",
|
|
217
|
+
"default": "./select-configs.js"
|
|
218
|
+
},
|
|
219
|
+
"./write-config-files": {
|
|
220
|
+
"types": "./write-config-files.d.ts",
|
|
221
|
+
"import": "./write-config-files.js",
|
|
222
|
+
"default": "./write-config-files.js"
|
|
223
|
+
},
|
|
224
|
+
"./write-config-files.js": {
|
|
225
|
+
"types": "./write-config-files.d.ts",
|
|
226
|
+
"import": "./write-config-files.js",
|
|
227
|
+
"default": "./write-config-files.js"
|
|
158
228
|
}
|
|
159
229
|
}
|
|
160
230
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { allConfigNames, defaultConfigNames } from './config-targets.js';
|
|
2
|
+
const isConfigName = (value) => {
|
|
3
|
+
return allConfigNames.includes(value);
|
|
4
|
+
};
|
|
5
|
+
export const parseConfigArgs = (args) => {
|
|
6
|
+
const dryRun = args.includes('--dry-run');
|
|
7
|
+
const force = args.includes('--force');
|
|
8
|
+
const help = args.includes('--help') || args.includes('-h');
|
|
9
|
+
const type = args.find((arg) => !arg.startsWith('-')) ?? 'init';
|
|
10
|
+
if (help) {
|
|
11
|
+
return {
|
|
12
|
+
dryRun,
|
|
13
|
+
force,
|
|
14
|
+
help,
|
|
15
|
+
names: [],
|
|
16
|
+
type: 'init',
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
if (type === 'init') {
|
|
20
|
+
return {
|
|
21
|
+
dryRun,
|
|
22
|
+
force,
|
|
23
|
+
help,
|
|
24
|
+
names: defaultConfigNames,
|
|
25
|
+
type,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (isConfigName(type)) {
|
|
29
|
+
return {
|
|
30
|
+
dryRun,
|
|
31
|
+
force,
|
|
32
|
+
help,
|
|
33
|
+
names: [type],
|
|
34
|
+
type,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
dryRun,
|
|
39
|
+
force,
|
|
40
|
+
help,
|
|
41
|
+
names: [],
|
|
42
|
+
type: 'unknown',
|
|
43
|
+
};
|
|
44
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline/promises';
|
|
2
|
+
import { allConfigNames, defaultConfigNames } from './config-targets.js';
|
|
3
|
+
const labels = {
|
|
4
|
+
eslint: 'ESLint',
|
|
5
|
+
jest: 'Jest',
|
|
6
|
+
prettier: 'Prettier',
|
|
7
|
+
storybook: 'Storybook',
|
|
8
|
+
stylelint: 'Stylelint',
|
|
9
|
+
typescript: 'TypeScript',
|
|
10
|
+
};
|
|
11
|
+
const parseAnswer = (answer) => {
|
|
12
|
+
const selected = new Set();
|
|
13
|
+
for (const token of answer
|
|
14
|
+
.split(',')
|
|
15
|
+
.map((part) => part.trim().toLowerCase())
|
|
16
|
+
.filter(Boolean)) {
|
|
17
|
+
const index = Number(token);
|
|
18
|
+
const configName = allConfigNames[index - 1];
|
|
19
|
+
if (configName) {
|
|
20
|
+
selected.add(configName);
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (allConfigNames.includes(token)) {
|
|
24
|
+
selected.add(token);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return [...selected];
|
|
28
|
+
};
|
|
29
|
+
export const selectConfigs = async () => {
|
|
30
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
31
|
+
return defaultConfigNames;
|
|
32
|
+
}
|
|
33
|
+
console.log('Select configs to create. Press enter for defaults.');
|
|
34
|
+
allConfigNames.forEach((name, index) => {
|
|
35
|
+
const defaultMarker = defaultConfigNames.includes(name) ? ' default' : '';
|
|
36
|
+
console.log(`${index + 1}. ${labels[name]}${defaultMarker}`);
|
|
37
|
+
});
|
|
38
|
+
const readline = createInterface({
|
|
39
|
+
input: process.stdin,
|
|
40
|
+
output: process.stdout,
|
|
41
|
+
});
|
|
42
|
+
try {
|
|
43
|
+
const answer = await readline.question('Configs: ');
|
|
44
|
+
const selected = parseAnswer(answer);
|
|
45
|
+
return selected.length > 0 ? selected : defaultConfigNames;
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
readline.close();
|
|
49
|
+
}
|
|
50
|
+
};
|
package/types.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type Command = {
|
|
2
2
|
readonly type: 'help' | 'version';
|
|
3
3
|
} | {
|
|
4
|
-
readonly type: 'check' | 'create' | 'dist' | 'static';
|
|
4
|
+
readonly type: 'check' | 'config' | 'create' | 'dist' | 'static';
|
|
5
5
|
readonly args: readonly string[];
|
|
6
6
|
} | {
|
|
7
7
|
readonly type: 'unknown';
|
|
@@ -9,3 +9,20 @@ export type Command = {
|
|
|
9
9
|
};
|
|
10
10
|
export type ParseArgs = (args: readonly string[]) => Command;
|
|
11
11
|
export type Cli = (args?: readonly string[]) => Promise<void>;
|
|
12
|
+
export type ConfigName = 'eslint' | 'jest' | 'prettier' | 'storybook' | 'stylelint' | 'typescript';
|
|
13
|
+
export type ConfigCommand = {
|
|
14
|
+
readonly dryRun: boolean;
|
|
15
|
+
readonly force: boolean;
|
|
16
|
+
readonly help: boolean;
|
|
17
|
+
readonly names: readonly ConfigName[];
|
|
18
|
+
readonly type: 'init' | ConfigName | 'unknown';
|
|
19
|
+
};
|
|
20
|
+
export type ConfigFile = {
|
|
21
|
+
readonly path: string;
|
|
22
|
+
readonly content: string;
|
|
23
|
+
};
|
|
24
|
+
export type ConfigTarget = {
|
|
25
|
+
readonly files: readonly ConfigFile[];
|
|
26
|
+
readonly name: ConfigName;
|
|
27
|
+
readonly packageName: string;
|
|
28
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ConfigFile } from './types.js';
|
|
2
|
+
type WriteConfigFilesOptions = {
|
|
3
|
+
readonly cwd: string;
|
|
4
|
+
readonly dryRun: boolean;
|
|
5
|
+
readonly exists: (path: string) => Promise<boolean>;
|
|
6
|
+
readonly files: readonly ConfigFile[];
|
|
7
|
+
readonly force: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare const writeConfigFiles: ({ cwd, dryRun, exists, files, force, }: WriteConfigFilesOptions) => Promise<readonly ConfigFile[]>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
export const writeConfigFiles = async ({ cwd, dryRun, exists, files, force, }) => {
|
|
4
|
+
const writable = [];
|
|
5
|
+
for (const file of files) {
|
|
6
|
+
if (!force && (await exists(join(cwd, file.path)))) {
|
|
7
|
+
console.log(`Skipped ${file.path} because it already exists.`);
|
|
8
|
+
console.log('Use --force to overwrite it.');
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
writable.push(file);
|
|
12
|
+
}
|
|
13
|
+
if (dryRun) {
|
|
14
|
+
if (writable.length > 0) {
|
|
15
|
+
console.log('Would create:');
|
|
16
|
+
for (const file of writable) {
|
|
17
|
+
console.log(`- ${file.path}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return writable;
|
|
21
|
+
}
|
|
22
|
+
for (const file of writable) {
|
|
23
|
+
const path = join(cwd, file.path);
|
|
24
|
+
await mkdir(dirname(path), { recursive: true });
|
|
25
|
+
await writeFile(path, file.content, 'utf8');
|
|
26
|
+
console.log(`${force ? 'Wrote' : 'Created'} ${file.path}.`);
|
|
27
|
+
}
|
|
28
|
+
return writable;
|
|
29
|
+
};
|