create-alistt69-kit 0.1.22 → 0.2.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 +34 -3
- package/package.json +1 -1
- package/src/core/parse-cli-args.js +1 -1
- package/src/core/render-project-readme.js +56 -14
- package/src/features/react-router/files/scripts/generate/page.mjs +341 -0
- package/src/features/react-router/files/src/app/providers/router/model/config/index.ts +1 -0
- package/src/features/react-router/files/src/app/providers/router/model/router/index.tsx +4 -1
- package/src/features/react-router/files/src/app/providers/router/types/index.ts +1 -0
- package/src/features/react-router/index.js +3 -0
package/README.md
CHANGED
|
@@ -53,15 +53,44 @@ Setting up a frontend project from scratch usually means repeating the same stuf
|
|
|
53
53
|
|
|
54
54
|
This starter removes that boilerplate so you can get straight to building.
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
## ⚡ Generated project helpers
|
|
57
|
+
|
|
58
|
+
Some optional features also add local project generators to the scaffolded app.
|
|
59
|
+
|
|
60
|
+
### Page generator
|
|
61
|
+
|
|
62
|
+
When `React Router DOM` is enabled, the project includes a built-in page generator.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm run generate:page about
|
|
66
|
+
```
|
|
67
|
+
This will create:
|
|
68
|
+
|
|
69
|
+
* `src/pages/about/index.ts`
|
|
70
|
+
* `src/pages/about/lazy.ts`
|
|
71
|
+
* `src/pages/about/page.tsx`
|
|
72
|
+
|
|
73
|
+
If the standard router files are present, the generator will also register the page automatically in:
|
|
74
|
+
|
|
75
|
+
* `src/app/providers/router/types/index.ts`
|
|
76
|
+
* `src/app/providers/router/model/config/index.ts`
|
|
77
|
+
* `src/app/providers/router/model/router/index.tsx`
|
|
78
|
+
|
|
79
|
+
You can also configure custom paths in `scripts/generate/page.mjs`:
|
|
80
|
+
* `ROUTER_TYPES_PATH`
|
|
81
|
+
* `ROUTER_CONFIG_PATH`
|
|
82
|
+
* `ROUTER_FILE_PATH`
|
|
83
|
+
|
|
84
|
+
#### Important
|
|
85
|
+
|
|
86
|
+
Route auto-registration relies on special marker comments `@route-...` inside the router files.
|
|
87
|
+
Do not remove these markers unless you also want to disable automatic updates.
|
|
57
88
|
|
|
58
89
|
## 📦 Requirements
|
|
59
90
|
|
|
60
91
|
- **Node.js** `18.18` or higher
|
|
61
92
|
- **npm**, **pnpm**, or **yarn**
|
|
62
93
|
|
|
63
|
-
---
|
|
64
|
-
|
|
65
94
|
## 🔥 Quick start
|
|
66
95
|
|
|
67
96
|
Create a new app interactively:
|
|
@@ -121,3 +150,5 @@ npm create alistt69-kit@latest my-app -- --defaults --overwrite
|
|
|
121
150
|
## 📄 License
|
|
122
151
|
|
|
123
152
|
MIT — free and open for everyone.
|
|
153
|
+
|
|
154
|
+
_See [LICENSE](./LICENSE)._
|
package/package.json
CHANGED
|
@@ -28,7 +28,7 @@ export function formatHelpMessage() {
|
|
|
28
28
|
' create-alistt69-kit <project-name> [options]',
|
|
29
29
|
'',
|
|
30
30
|
'Options:',
|
|
31
|
-
' -def, --defaults
|
|
31
|
+
' -def, --defaults Skip prompts and use defaults',
|
|
32
32
|
' --overwrite Overwrite target directory if it exists',
|
|
33
33
|
' --no-install Do not install dependencies',
|
|
34
34
|
' --features <comma-list> Example: eslint,stylelint,react-router',
|
|
@@ -49,6 +49,7 @@ const scriptDescriptions = {
|
|
|
49
49
|
'lint:fix': 'Run ESLint with autofix',
|
|
50
50
|
'lint:styles': 'Run Stylelint',
|
|
51
51
|
'lint:styles:fix': 'Run Stylelint with autofix',
|
|
52
|
+
'generate:page': 'Generate a new page and auto-register it in the router when available',
|
|
52
53
|
};
|
|
53
54
|
|
|
54
55
|
function formatMarkdownList(items, fallback = '- None') {
|
|
@@ -105,10 +106,10 @@ function formatScripts(packageManager, scripts) {
|
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
function formatQuickStart({
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
projectName,
|
|
110
|
+
packageManager,
|
|
111
|
+
shouldInstallDependencies,
|
|
112
|
+
}) {
|
|
112
113
|
const steps = [`cd ${projectName}`];
|
|
113
114
|
|
|
114
115
|
if (!shouldInstallDependencies) {
|
|
@@ -128,24 +129,64 @@ function formatQuickStart({
|
|
|
128
129
|
].join('\n');
|
|
129
130
|
}
|
|
130
131
|
|
|
131
|
-
function formatProjectStructure() {
|
|
132
|
-
|
|
132
|
+
function formatProjectStructure(selectedFeatureIds) {
|
|
133
|
+
const items = [
|
|
133
134
|
'- `public/` — static assets and HTML template',
|
|
134
135
|
'- `src/` — application source code',
|
|
135
136
|
'- `src/app/` — app bootstrap, providers, entry-level setup',
|
|
136
137
|
'- `src/styles/` — global styles and shared styling layer',
|
|
137
138
|
'- `config/build/` — split webpack configuration',
|
|
138
|
-
]
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
if (selectedFeatureIds.includes('react-router')) {
|
|
142
|
+
items.splice(3, 0, '- `src/pages/` — route-level pages');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return items.join('\n');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function formatFeatureSpecificSections({ selectedFeatureIds, packageManager }) {
|
|
149
|
+
const sections = [];
|
|
150
|
+
|
|
151
|
+
if (selectedFeatureIds.includes('react-router')) {
|
|
152
|
+
sections.push([
|
|
153
|
+
'## Page generator',
|
|
154
|
+
'',
|
|
155
|
+
'When `React Router DOM` is enabled, you can scaffold a new page with one command.',
|
|
156
|
+
'',
|
|
157
|
+
'```bash',
|
|
158
|
+
`${getRunScriptCommand(packageManager, 'generate:page')} about`,
|
|
159
|
+
'```',
|
|
160
|
+
'',
|
|
161
|
+
'This creates:',
|
|
162
|
+
'',
|
|
163
|
+
'- `src/pages/about/index.ts`',
|
|
164
|
+
'- `src/pages/about/lazy.ts`',
|
|
165
|
+
'- `src/pages/about/page.tsx`',
|
|
166
|
+
'',
|
|
167
|
+
'If the standard router files are still present, the generator also updates:',
|
|
168
|
+
'',
|
|
169
|
+
'- `src/app/providers/router/types/index.ts`',
|
|
170
|
+
'- `src/app/providers/router/model/config/index.ts`',
|
|
171
|
+
'- `src/app/providers/router/model/router/index.tsx`',
|
|
172
|
+
].join('\n'));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return sections.join('\n\n');
|
|
139
176
|
}
|
|
140
177
|
|
|
141
178
|
export async function renderProjectReadme({
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
179
|
+
projectPath,
|
|
180
|
+
projectName,
|
|
181
|
+
selectedFeatureIds,
|
|
182
|
+
packageManager,
|
|
183
|
+
shouldInstallDependencies,
|
|
184
|
+
}) {
|
|
148
185
|
const packageJson = await readPackageJson(projectPath);
|
|
186
|
+
const featureSpecificSections = formatFeatureSpecificSections({
|
|
187
|
+
selectedFeatureIds,
|
|
188
|
+
packageManager,
|
|
189
|
+
});
|
|
149
190
|
|
|
150
191
|
const readmeContent = [
|
|
151
192
|
`# ${projectName}`,
|
|
@@ -174,11 +215,12 @@ export async function renderProjectReadme({
|
|
|
174
215
|
'',
|
|
175
216
|
'## Project structure',
|
|
176
217
|
'',
|
|
177
|
-
formatProjectStructure(),
|
|
218
|
+
formatProjectStructure(selectedFeatureIds),
|
|
178
219
|
'',
|
|
179
220
|
'## Available scripts',
|
|
180
221
|
'',
|
|
181
222
|
formatScripts(packageManager, packageJson.scripts ?? {}),
|
|
223
|
+
...(featureSpecificSections ? ['', featureSpecificSections] : []),
|
|
182
224
|
].join('\n');
|
|
183
225
|
|
|
184
226
|
await writeFile(
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { access, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
const ROUTER_TYPES_PATH = path.join(
|
|
5
|
+
'src',
|
|
6
|
+
'app',
|
|
7
|
+
'providers',
|
|
8
|
+
'router',
|
|
9
|
+
'types',
|
|
10
|
+
'index.ts',
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const ROUTER_CONFIG_PATH = path.join(
|
|
14
|
+
'src',
|
|
15
|
+
'app',
|
|
16
|
+
'providers',
|
|
17
|
+
'router',
|
|
18
|
+
'model',
|
|
19
|
+
'config',
|
|
20
|
+
'index.ts',
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const ROUTER_FILE_PATH = path.join(
|
|
24
|
+
'src',
|
|
25
|
+
'app',
|
|
26
|
+
'providers',
|
|
27
|
+
'router',
|
|
28
|
+
'model',
|
|
29
|
+
'router',
|
|
30
|
+
'index.tsx',
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
async function main() {
|
|
34
|
+
const rawPageName = process.argv[2];
|
|
35
|
+
|
|
36
|
+
if (!rawPageName) {
|
|
37
|
+
printUsageAndExit();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const pageSlug = normalizePageSlug(rawPageName);
|
|
41
|
+
|
|
42
|
+
if (!pageSlug) {
|
|
43
|
+
fail('Page name cannot be empty.');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const pageDirPath = path.join(process.cwd(), 'src', 'pages', pageSlug);
|
|
47
|
+
|
|
48
|
+
if (await pathExists(pageDirPath)) {
|
|
49
|
+
fail(`Page "${pageSlug}" already exists: ${toRelativeProjectPath(pageDirPath)}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const pageMeta = buildPageMeta(pageSlug);
|
|
53
|
+
|
|
54
|
+
await createPageFiles(pageDirPath, pageMeta);
|
|
55
|
+
|
|
56
|
+
const autoRegisterResult = await autoRegisterPage(pageMeta);
|
|
57
|
+
|
|
58
|
+
console.log(`✔ Page "${pageSlug}" created.`);
|
|
59
|
+
|
|
60
|
+
if (autoRegisterResult.status === 'registered') {
|
|
61
|
+
console.log('✔ Route registration updated.');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log(`ℹ ${autoRegisterResult.message}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function printUsageAndExit() {
|
|
69
|
+
console.error('Usage: npm run generate:page -- <page-name>');
|
|
70
|
+
console.error('Example: npm run generate:page -- about');
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function fail(message) {
|
|
75
|
+
console.error(`✖ ${message}`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function normalizePageSlug(value) {
|
|
80
|
+
return value
|
|
81
|
+
.trim()
|
|
82
|
+
.toLowerCase()
|
|
83
|
+
.replace(/[_\s]+/g, '-')
|
|
84
|
+
.replace(/[^a-z0-9-]/g, '')
|
|
85
|
+
.replace(/-+/g, '-')
|
|
86
|
+
.replace(/^-+|-+$/g, '');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function buildPageMeta(pageSlug) {
|
|
90
|
+
return {
|
|
91
|
+
pageSlug,
|
|
92
|
+
pageComponentName: toPascalCase(pageSlug),
|
|
93
|
+
pageTitle: toTitleCase(pageSlug),
|
|
94
|
+
routeEnumKey: toScreamingSnake(pageSlug),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function toPascalCase(value) {
|
|
99
|
+
return value
|
|
100
|
+
.split('-')
|
|
101
|
+
.filter(Boolean)
|
|
102
|
+
.map((part) => part[0].toUpperCase() + part.slice(1))
|
|
103
|
+
.join('');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function toScreamingSnake(value) {
|
|
107
|
+
return value
|
|
108
|
+
.split('-')
|
|
109
|
+
.filter(Boolean)
|
|
110
|
+
.join('_')
|
|
111
|
+
.toUpperCase();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function toTitleCase(value) {
|
|
115
|
+
return value
|
|
116
|
+
.split('-')
|
|
117
|
+
.filter(Boolean)
|
|
118
|
+
.map((part) => part[0].toUpperCase() + part.slice(1))
|
|
119
|
+
.join(' ');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function createPageFiles(pageDirPath, pageMeta) {
|
|
123
|
+
const { pageComponentName, pageTitle } = pageMeta;
|
|
124
|
+
|
|
125
|
+
await mkdir(pageDirPath, {
|
|
126
|
+
recursive: true,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
await writeFile(
|
|
130
|
+
path.join(pageDirPath, 'index.ts'),
|
|
131
|
+
`export { Lazy${pageComponentName} as ${pageComponentName} } from './lazy';\n`,
|
|
132
|
+
'utf8',
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
await writeFile(
|
|
136
|
+
path.join(pageDirPath, 'lazy.ts'),
|
|
137
|
+
[
|
|
138
|
+
"import { lazy } from 'react';",
|
|
139
|
+
'',
|
|
140
|
+
`export const Lazy${pageComponentName} = lazy(() => import('./page'));`,
|
|
141
|
+
'',
|
|
142
|
+
].join('\n'),
|
|
143
|
+
'utf8',
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
await writeFile(
|
|
147
|
+
path.join(pageDirPath, 'page.tsx'),
|
|
148
|
+
[
|
|
149
|
+
`function ${pageComponentName}Page() {`,
|
|
150
|
+
' return (',
|
|
151
|
+
` <h2>${pageTitle} page</h2>`,
|
|
152
|
+
' );',
|
|
153
|
+
'}',
|
|
154
|
+
'',
|
|
155
|
+
`export default ${pageComponentName}Page;`,
|
|
156
|
+
'',
|
|
157
|
+
].join('\n'),
|
|
158
|
+
'utf8',
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function autoRegisterPage(pageMeta) {
|
|
163
|
+
const projectRoot = process.cwd();
|
|
164
|
+
|
|
165
|
+
const typesFilePath = path.join(projectRoot, ROUTER_TYPES_PATH);
|
|
166
|
+
const configFilePath = path.join(projectRoot, ROUTER_CONFIG_PATH);
|
|
167
|
+
const routerFilePath = path.join(projectRoot, ROUTER_FILE_PATH);
|
|
168
|
+
|
|
169
|
+
const [hasTypesFile, hasConfigFile, hasRouterFile] = await Promise.all([
|
|
170
|
+
pathExists(typesFilePath),
|
|
171
|
+
pathExists(configFilePath),
|
|
172
|
+
pathExists(routerFilePath),
|
|
173
|
+
]);
|
|
174
|
+
|
|
175
|
+
if (!hasTypesFile || !hasConfigFile || !hasRouterFile) {
|
|
176
|
+
const missingFiles = [
|
|
177
|
+
!hasTypesFile ? ROUTER_TYPES_PATH : null,
|
|
178
|
+
!hasConfigFile ? ROUTER_CONFIG_PATH : null,
|
|
179
|
+
!hasRouterFile ? ROUTER_FILE_PATH : null,
|
|
180
|
+
].filter(Boolean);
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
status: 'skipped',
|
|
184
|
+
message: `Route auto-registration skipped. Missing files: ${missingFiles.join(', ')}`,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
await insertRouteEnum(typesFilePath, pageMeta);
|
|
189
|
+
await insertRouteConfig(configFilePath, pageMeta);
|
|
190
|
+
await insertRouteImport(routerFilePath, pageMeta);
|
|
191
|
+
await insertRouteDefinition(routerFilePath, pageMeta);
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
status: 'registered',
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function insertRouteEnum(filepath, pageMeta) {
|
|
199
|
+
await insertBeforeMarkerLine({
|
|
200
|
+
filepath,
|
|
201
|
+
marker: '// @route-enum',
|
|
202
|
+
block: `${pageMeta.routeEnumKey} = '${pageMeta.pageSlug}',`,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function insertRouteConfig(filepath, pageMeta) {
|
|
207
|
+
await insertBeforeMarkerLine({
|
|
208
|
+
filepath,
|
|
209
|
+
marker: '// @route-config',
|
|
210
|
+
block: [
|
|
211
|
+
`[ERoutePath.${pageMeta.routeEnumKey}]: {`,
|
|
212
|
+
` path: ERoutePath.${pageMeta.routeEnumKey},`,
|
|
213
|
+
` title: '${pageMeta.pageTitle}',`,
|
|
214
|
+
'},',
|
|
215
|
+
].join('\n'),
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function insertRouteImport(filepath, pageMeta) {
|
|
220
|
+
await insertBeforeMarkerLine({
|
|
221
|
+
filepath,
|
|
222
|
+
marker: '/* @route-imports */',
|
|
223
|
+
block: `import { ${pageMeta.pageComponentName} } from '../../../../../pages/${pageMeta.pageSlug}';`,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async function insertRouteDefinition(filepath, pageMeta) {
|
|
228
|
+
await insertBeforeMarkerLine({
|
|
229
|
+
filepath,
|
|
230
|
+
marker: '{/* @route-routes */}',
|
|
231
|
+
block: `<Route path={ERoutePath.${pageMeta.routeEnumKey}} element={<${pageMeta.pageComponentName} />} />`,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function insertBeforeMarkerLine({ filepath, marker, block }) {
|
|
236
|
+
const fileContent = await readFile(filepath, 'utf8');
|
|
237
|
+
const eol = detectEol(fileContent);
|
|
238
|
+
const hadTrailingNewline = /\r?\n$/.test(fileContent);
|
|
239
|
+
|
|
240
|
+
const lines = fileContent.split(/\r?\n/);
|
|
241
|
+
|
|
242
|
+
if (hadTrailingNewline && lines.at(-1) === '') {
|
|
243
|
+
lines.pop();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const markerLineIndex = lines.findIndex((line) => line.includes(marker));
|
|
247
|
+
|
|
248
|
+
if (markerLineIndex === -1) {
|
|
249
|
+
fail(`Marker "${marker}" not found in ${toRelativeProjectPath(filepath)}`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const markerLine = lines[markerLineIndex];
|
|
253
|
+
const baseIndent = getLineIndent(markerLine);
|
|
254
|
+
const normalizedBlock = indentBlock(block, baseIndent);
|
|
255
|
+
const normalizedBlockLines = normalizedBlock.split('\n');
|
|
256
|
+
|
|
257
|
+
if (blockAlreadyInserted(lines, normalizedBlockLines)) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
lines.splice(markerLineIndex, 0, ...normalizedBlockLines);
|
|
262
|
+
|
|
263
|
+
let nextContent = lines.join(eol);
|
|
264
|
+
|
|
265
|
+
if (hadTrailingNewline) {
|
|
266
|
+
nextContent += eol;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
await writeFile(filepath, nextContent, 'utf8');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function detectEol(content) {
|
|
273
|
+
return content.includes('\r\n') ? '\r\n' : '\n';
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function getLineIndent(line) {
|
|
277
|
+
const match = line.match(/^\s*/);
|
|
278
|
+
|
|
279
|
+
return match ? match[0] : '';
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function indentBlock(block, baseIndent) {
|
|
283
|
+
const rawLines = block.split('\n');
|
|
284
|
+
|
|
285
|
+
return rawLines
|
|
286
|
+
.map((line) => {
|
|
287
|
+
if (!line.trim()) {
|
|
288
|
+
return '';
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const lineIndentSize = countLeadingSpaces(line);
|
|
292
|
+
const relativeIndentLevel = Math.floor(lineIndentSize / 4);
|
|
293
|
+
const relativeIndent = ' '.repeat(relativeIndentLevel * 4);
|
|
294
|
+
|
|
295
|
+
return `${baseIndent}${relativeIndent}${line.trimStart()}`;
|
|
296
|
+
})
|
|
297
|
+
.join('\n');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function countLeadingSpaces(line) {
|
|
301
|
+
const match = line.match(/^ */);
|
|
302
|
+
|
|
303
|
+
return match ? match[0].length : 0;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function blockAlreadyInserted(lines, normalizedBlockLines) {
|
|
307
|
+
const comparableLines = normalizedBlockLines.filter((line) => line.trim() !== '');
|
|
308
|
+
|
|
309
|
+
if (comparableLines.length === 0) {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
for (let index = 0; index <= lines.length - comparableLines.length; index += 1) {
|
|
314
|
+
const currentSlice = lines.slice(index, index + comparableLines.length);
|
|
315
|
+
|
|
316
|
+
if (currentSlice.join('\n') === comparableLines.join('\n')) {
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async function pathExists(filepath) {
|
|
325
|
+
try {
|
|
326
|
+
await access(filepath);
|
|
327
|
+
return true;
|
|
328
|
+
} catch (_) {
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function toRelativeProjectPath(filepath) {
|
|
334
|
+
return path.relative(process.cwd(), filepath) || '.';
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
main().catch((error) => {
|
|
338
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
339
|
+
|
|
340
|
+
fail(message);
|
|
341
|
+
});
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { ComponentType, ReactNode } from 'react';
|
|
2
2
|
import { createBrowserRouter, createRoutesFromElements, Route } from 'react-router-dom';
|
|
3
|
+
import { ERoutePath } from '../../types';
|
|
3
4
|
import { Error } from '../../../../../pages/error';
|
|
4
5
|
import { Main } from '../../../../../pages/main';
|
|
6
|
+
/* @route-imports */
|
|
5
7
|
import AppLayout from '../../ui/app';
|
|
6
8
|
|
|
7
9
|
export const getRouter = (ErrorBoundary: ComponentType<{ children: ReactNode }>) => createBrowserRouter(
|
|
@@ -15,7 +17,8 @@ export const getRouter = (ErrorBoundary: ComponentType<{ children: ReactNode }>)
|
|
|
15
17
|
)}
|
|
16
18
|
>
|
|
17
19
|
<Route index element={<Main />} />
|
|
18
|
-
|
|
20
|
+
{/* @route-routes */}
|
|
21
|
+
<Route path={ERoutePath.ERROR} element={<Error />} />
|
|
19
22
|
</Route>,
|
|
20
23
|
),
|
|
21
24
|
);
|