create-analog 3.0.0-alpha.19 → 3.0.0-alpha.20
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/index.js +315 -150
- package/package.json +3 -3
- package/template-angular-v17/package.json +5 -5
- package/template-angular-v18/package.json +5 -5
- package/template-angular-v19/package.json +5 -5
- package/template-angular-v20/package.json +5 -5
- package/template-blog/package.json +5 -5
- package/template-blog/src/app/app.config.ts +6 -2
- package/template-latest/package.json +5 -5
- package/template-minimal/package.json +5 -5
package/index.js
CHANGED
|
@@ -3,17 +3,73 @@
|
|
|
3
3
|
// @ts-check
|
|
4
4
|
import { blue, green, red, reset, yellow } from 'kolorist';
|
|
5
5
|
import minimist from 'minimist';
|
|
6
|
-
import {
|
|
6
|
+
import { execFileSync } from 'node:child_process';
|
|
7
7
|
import fs from 'node:fs';
|
|
8
8
|
import path from 'node:path';
|
|
9
9
|
import { fileURLToPath } from 'node:url';
|
|
10
10
|
import prompts from 'prompts';
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {'latest' | 'blog' | 'minimal'} Template
|
|
14
|
+
* @typedef {'prismjs' | 'shiki'} HighlighterId
|
|
15
|
+
* @typedef {(value: string) => string} Colorizer
|
|
16
|
+
*
|
|
17
|
+
* @typedef {object} Variant
|
|
18
|
+
* @property {string} name
|
|
19
|
+
* @property {Template} template
|
|
20
|
+
* @property {Colorizer} color
|
|
21
|
+
*
|
|
22
|
+
* @typedef {object} AppDefinition
|
|
23
|
+
* @property {string} name
|
|
24
|
+
* @property {Colorizer} color
|
|
25
|
+
* @property {readonly Variant[]} variants
|
|
26
|
+
*
|
|
27
|
+
* @typedef {object} HighlighterConfig
|
|
28
|
+
* @property {string} highlighter
|
|
29
|
+
* @property {string} entryPoint
|
|
30
|
+
* @property {Record<string, string>} dependencies
|
|
31
|
+
*
|
|
32
|
+
* @typedef {object} PackageJson
|
|
33
|
+
* @property {string} [name]
|
|
34
|
+
* @property {Record<string, string>} [scripts]
|
|
35
|
+
* @property {Record<string, string>} [dependencies]
|
|
36
|
+
* @property {Record<string, string>} [devDependencies]
|
|
37
|
+
*
|
|
38
|
+
* @typedef {object} PromptAnswers
|
|
39
|
+
* @property {string} [projectName]
|
|
40
|
+
* @property {boolean} [overwrite]
|
|
41
|
+
* @property {string} [packageName]
|
|
42
|
+
* @property {Template} [variant]
|
|
43
|
+
* @property {boolean} [tailwind]
|
|
44
|
+
* @property {HighlighterId} [syntaxHighlighter]
|
|
45
|
+
*
|
|
46
|
+
* @typedef {object} UserAgentPackage
|
|
47
|
+
* @property {string} name
|
|
48
|
+
* @property {string} version
|
|
49
|
+
*
|
|
50
|
+
* @typedef {{
|
|
51
|
+
* _: string[];
|
|
52
|
+
* template?: string;
|
|
53
|
+
* t?: string;
|
|
54
|
+
* skipTailwind?: boolean | string;
|
|
55
|
+
* skipGit?: boolean | string;
|
|
56
|
+
* } & Record<string, unknown>} CliArgv
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
const CLI_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
60
|
+
const DEFAULT_TARGET_DIR = 'analog-project';
|
|
61
|
+
const DEFAULT_BLOG_HIGHLIGHTER = 'prismjs';
|
|
62
|
+
|
|
63
|
+
/** @type {readonly Template[]} */
|
|
64
|
+
const H3_TEMPLATES = ['latest', 'blog', 'minimal'];
|
|
65
|
+
|
|
12
66
|
// Avoids autoconversion to number of the project name by defining that the args
|
|
13
67
|
// non associated with an option ( _ ) needs to be parsed as a string. See #4606
|
|
68
|
+
/** @type {CliArgv} */
|
|
14
69
|
const argv = minimist(process.argv.slice(2), { string: ['_'] });
|
|
15
70
|
const cwd = process.cwd();
|
|
16
71
|
|
|
72
|
+
/** @type {readonly AppDefinition[]} */
|
|
17
73
|
const APPS = [
|
|
18
74
|
{
|
|
19
75
|
name: 'Analog',
|
|
@@ -37,6 +93,8 @@ const APPS = [
|
|
|
37
93
|
],
|
|
38
94
|
},
|
|
39
95
|
];
|
|
96
|
+
|
|
97
|
+
/** @type {Readonly<Record<HighlighterId, HighlighterConfig>>} */
|
|
40
98
|
const HIGHLIGHTERS = {
|
|
41
99
|
prismjs: {
|
|
42
100
|
highlighter: 'withPrismHighlighter',
|
|
@@ -57,111 +115,120 @@ const HIGHLIGHTERS = {
|
|
|
57
115
|
},
|
|
58
116
|
};
|
|
59
117
|
|
|
118
|
+
/** @type {Readonly<Record<string, string>>} */
|
|
60
119
|
const renameFiles = {
|
|
61
120
|
_gitignore: '.gitignore',
|
|
62
121
|
};
|
|
63
122
|
|
|
64
123
|
async function init() {
|
|
65
124
|
let targetDir = formatTargetDir(argv._[0]);
|
|
66
|
-
let template = argv.template
|
|
125
|
+
let template = resolveTemplate(argv.template ?? argv.t);
|
|
67
126
|
let skipTailwind = fromBoolArg(argv.skipTailwind);
|
|
127
|
+
const skipGit = fromBoolArg(argv.skipGit ?? argv['skip-git']) ?? false;
|
|
68
128
|
|
|
69
|
-
const defaultTargetDir = 'analog-project';
|
|
70
129
|
const getProjectName = () =>
|
|
71
|
-
targetDir === '.' ? path.basename(path.resolve()) : targetDir;
|
|
130
|
+
targetDir === '.' ? path.basename(path.resolve()) : (targetDir ?? '');
|
|
72
131
|
|
|
132
|
+
/** @type {PromptAnswers} */
|
|
73
133
|
let result = {};
|
|
74
134
|
|
|
75
135
|
try {
|
|
76
|
-
result =
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
136
|
+
result = /** @type {PromptAnswers} */ (
|
|
137
|
+
await prompts(
|
|
138
|
+
[
|
|
139
|
+
{
|
|
140
|
+
type: targetDir ? null : 'text',
|
|
141
|
+
name: 'projectName',
|
|
142
|
+
message: reset('Project name:'),
|
|
143
|
+
initial: DEFAULT_TARGET_DIR,
|
|
144
|
+
onState: (state) => {
|
|
145
|
+
targetDir =
|
|
146
|
+
formatTargetDir(String(state.value ?? '')) ||
|
|
147
|
+
DEFAULT_TARGET_DIR;
|
|
148
|
+
},
|
|
85
149
|
},
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
{
|
|
98
|
-
type: (_, { overwrite } = {}) => {
|
|
99
|
-
if (overwrite === false) {
|
|
100
|
-
throw new Error(red('✖') + ' Operation cancelled');
|
|
101
|
-
}
|
|
102
|
-
return null;
|
|
150
|
+
{
|
|
151
|
+
type: () =>
|
|
152
|
+
!targetDir || !fs.existsSync(targetDir) || isEmpty(targetDir)
|
|
153
|
+
? null
|
|
154
|
+
: 'confirm',
|
|
155
|
+
name: 'overwrite',
|
|
156
|
+
message: () =>
|
|
157
|
+
(targetDir === '.'
|
|
158
|
+
? 'Current directory'
|
|
159
|
+
: `Target directory "${targetDir}"`) +
|
|
160
|
+
' is not empty. Remove existing files and continue?',
|
|
103
161
|
},
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
162
|
+
{
|
|
163
|
+
type: (_, promptState = {}) => {
|
|
164
|
+
if (promptState.overwrite === false) {
|
|
165
|
+
throw new Error(`${red('✖')} Operation cancelled`);
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
},
|
|
169
|
+
name: 'overwriteChecker',
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
type: () => (isValidPackageName(getProjectName()) ? null : 'text'),
|
|
173
|
+
name: 'packageName',
|
|
174
|
+
message: reset('Package name:'),
|
|
175
|
+
initial: () => toValidPackageName(getProjectName()),
|
|
176
|
+
validate: (dir) =>
|
|
177
|
+
isValidPackageName(String(dir)) || 'Invalid package.json name',
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
type: template ? null : 'select',
|
|
181
|
+
name: 'variant',
|
|
182
|
+
message: reset('What would you like to start?:'),
|
|
183
|
+
choices: APPS[0].variants.map((variant) => ({
|
|
184
|
+
title: variant.color(variant.name),
|
|
123
185
|
value: variant.template,
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
186
|
+
})),
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
type: (prev) => (prev === 'blog' ? 'select' : null),
|
|
190
|
+
name: 'syntaxHighlighter',
|
|
191
|
+
message: reset('Choose a syntax highlighter:'),
|
|
192
|
+
choices:
|
|
193
|
+
/** @type {{ title: HighlighterId; value: HighlighterId }[]} */ (
|
|
194
|
+
Object.keys(HIGHLIGHTERS).map((highlighter) => ({
|
|
195
|
+
title: /** @type {HighlighterId} */ (highlighter),
|
|
196
|
+
value: /** @type {HighlighterId} */ (highlighter),
|
|
197
|
+
}))
|
|
198
|
+
),
|
|
199
|
+
initial: 1,
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
type: skipTailwind === undefined ? 'confirm' : null,
|
|
203
|
+
name: 'tailwind',
|
|
204
|
+
message: 'Would you like to add Tailwind to your project?',
|
|
205
|
+
},
|
|
206
|
+
],
|
|
137
207
|
{
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
},
|
|
142
|
-
],
|
|
143
|
-
{
|
|
144
|
-
onCancel: () => {
|
|
145
|
-
throw new Error(red('✖') + ' Operation cancelled');
|
|
208
|
+
onCancel: () => {
|
|
209
|
+
throw new Error(`${red('✖')} Operation cancelled`);
|
|
210
|
+
},
|
|
146
211
|
},
|
|
147
|
-
|
|
212
|
+
)
|
|
148
213
|
);
|
|
149
|
-
} catch (
|
|
150
|
-
console.log(
|
|
214
|
+
} catch (error) {
|
|
215
|
+
console.log(error instanceof Error ? error.message : String(error));
|
|
151
216
|
return;
|
|
152
217
|
}
|
|
153
218
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
framework,
|
|
157
|
-
overwrite,
|
|
158
|
-
packageName,
|
|
159
|
-
variant,
|
|
160
|
-
tailwind,
|
|
161
|
-
syntaxHighlighter,
|
|
162
|
-
} = result;
|
|
219
|
+
const { overwrite, packageName, variant, tailwind, syntaxHighlighter } =
|
|
220
|
+
result;
|
|
163
221
|
|
|
164
|
-
|
|
222
|
+
template = variant ?? template;
|
|
223
|
+
if (!template) {
|
|
224
|
+
throw new Error('A project template must be selected.');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const highlighter =
|
|
228
|
+
syntaxHighlighter ??
|
|
229
|
+
(template === 'blog' ? DEFAULT_BLOG_HIGHLIGHTER : undefined);
|
|
230
|
+
|
|
231
|
+
const root = path.join(cwd, targetDir ?? DEFAULT_TARGET_DIR);
|
|
165
232
|
|
|
166
233
|
if (overwrite) {
|
|
167
234
|
emptyDir(root);
|
|
@@ -169,37 +236,32 @@ async function init() {
|
|
|
169
236
|
fs.mkdirSync(root, { recursive: true });
|
|
170
237
|
}
|
|
171
238
|
|
|
172
|
-
// determine template
|
|
173
|
-
template = variant || framework || template;
|
|
174
|
-
// determine syntax highlighter
|
|
175
|
-
const highlighter =
|
|
176
|
-
syntaxHighlighter ?? (template === 'blog' ? 'prism' : null);
|
|
177
239
|
skipTailwind = skipTailwind ?? !tailwind;
|
|
178
240
|
|
|
179
241
|
console.log(`\nScaffolding project in ${root}...`);
|
|
180
242
|
|
|
181
|
-
const templateDir = path.resolve(
|
|
182
|
-
|
|
183
|
-
'..',
|
|
184
|
-
`template-${template}`,
|
|
185
|
-
);
|
|
186
|
-
|
|
187
|
-
const filesDir = path.resolve(fileURLToPath(import.meta.url), '..', `files`);
|
|
243
|
+
const templateDir = path.resolve(CLI_DIR, `template-${template}`);
|
|
244
|
+
const filesDir = path.resolve(CLI_DIR, 'files');
|
|
188
245
|
|
|
246
|
+
/**
|
|
247
|
+
* @param {string} file
|
|
248
|
+
* @param {string | undefined} [content]
|
|
249
|
+
*/
|
|
189
250
|
const write = (file, content) => {
|
|
190
251
|
const targetPath = renameFiles[file]
|
|
191
252
|
? path.join(root, renameFiles[file])
|
|
192
253
|
: path.join(root, file);
|
|
193
254
|
|
|
194
|
-
if (content) {
|
|
255
|
+
if (typeof content === 'string') {
|
|
195
256
|
fs.writeFileSync(targetPath, content);
|
|
196
|
-
|
|
197
|
-
copy(path.join(templateDir, file), targetPath);
|
|
257
|
+
return;
|
|
198
258
|
}
|
|
259
|
+
|
|
260
|
+
copy(path.join(templateDir, file), targetPath);
|
|
199
261
|
};
|
|
200
262
|
|
|
201
263
|
const files = fs.readdirSync(templateDir);
|
|
202
|
-
for (const file of files.filter((
|
|
264
|
+
for (const file of files.filter((entry) => entry !== 'package.json')) {
|
|
203
265
|
write(file);
|
|
204
266
|
}
|
|
205
267
|
|
|
@@ -209,18 +271,22 @@ async function init() {
|
|
|
209
271
|
|
|
210
272
|
replacePlaceholders(root, 'vite.config.ts', {
|
|
211
273
|
__TAILWIND_IMPORT__: !skipTailwind
|
|
212
|
-
?
|
|
274
|
+
? "import tailwindcss from '@tailwindcss/vite';\n"
|
|
213
275
|
: '',
|
|
214
276
|
__TAILWIND_PLUGIN__: !skipTailwind ? ' tailwindcss(),\n' : '',
|
|
215
277
|
});
|
|
216
278
|
|
|
217
|
-
|
|
218
|
-
const pkgManager = pkgInfo ? pkgInfo.name : 'npm';
|
|
279
|
+
/** @type {PackageJson} */
|
|
219
280
|
const pkg = JSON.parse(
|
|
220
|
-
fs.readFileSync(path.join(templateDir,
|
|
281
|
+
fs.readFileSync(path.join(templateDir, 'package.json'), 'utf-8'),
|
|
221
282
|
);
|
|
283
|
+
const pkgManager =
|
|
284
|
+
pkgFromUserAgent(process.env.npm_config_user_agent)?.name ?? 'npm';
|
|
222
285
|
|
|
223
286
|
pkg.name = packageName || getProjectName();
|
|
287
|
+
pkg.scripts ??= {};
|
|
288
|
+
pkg.dependencies ??= {};
|
|
289
|
+
pkg.devDependencies ??= {};
|
|
224
290
|
pkg.scripts.start = getStartCommand(pkgManager);
|
|
225
291
|
|
|
226
292
|
if (template === 'blog' && highlighter) {
|
|
@@ -246,18 +312,22 @@ async function init() {
|
|
|
246
312
|
|
|
247
313
|
setProjectTitle(root, getProjectName());
|
|
248
314
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
315
|
+
if (!skipGit) {
|
|
316
|
+
console.log('\nInitializing git repository:');
|
|
317
|
+
execFileSync('git', ['init', targetDir], { stdio: 'inherit' });
|
|
318
|
+
execFileSync('git', ['-C', targetDir, 'add', '.'], { stdio: 'inherit' });
|
|
319
|
+
|
|
320
|
+
// Can fail when the user does not have global git credentials.
|
|
321
|
+
try {
|
|
322
|
+
execFileSync('git', ['-C', targetDir, 'commit', '-m', 'initial commit'], {
|
|
323
|
+
stdio: 'inherit',
|
|
324
|
+
});
|
|
325
|
+
} catch {
|
|
326
|
+
/* ignore */
|
|
327
|
+
}
|
|
258
328
|
}
|
|
259
329
|
|
|
260
|
-
console.log(
|
|
330
|
+
console.log('\nDone. Now run:\n');
|
|
261
331
|
if (root !== cwd) {
|
|
262
332
|
console.log(` cd ${path.relative(cwd, root)}`);
|
|
263
333
|
}
|
|
@@ -268,22 +338,30 @@ async function init() {
|
|
|
268
338
|
|
|
269
339
|
/**
|
|
270
340
|
* @param {string | undefined} targetDir
|
|
341
|
+
* @returns {string | undefined}
|
|
271
342
|
*/
|
|
272
343
|
function formatTargetDir(targetDir) {
|
|
273
344
|
return targetDir?.trim().replace(/\/+$/g, '');
|
|
274
345
|
}
|
|
275
346
|
|
|
347
|
+
/**
|
|
348
|
+
* @param {string} src
|
|
349
|
+
* @param {string} dest
|
|
350
|
+
* @returns {void}
|
|
351
|
+
*/
|
|
276
352
|
function copy(src, dest) {
|
|
277
353
|
const stat = fs.statSync(src);
|
|
278
354
|
if (stat.isDirectory()) {
|
|
279
355
|
copyDir(src, dest);
|
|
280
|
-
|
|
281
|
-
fs.copyFileSync(src, dest);
|
|
356
|
+
return;
|
|
282
357
|
}
|
|
358
|
+
|
|
359
|
+
fs.copyFileSync(src, dest);
|
|
283
360
|
}
|
|
284
361
|
|
|
285
362
|
/**
|
|
286
363
|
* @param {string} projectName
|
|
364
|
+
* @returns {boolean}
|
|
287
365
|
*/
|
|
288
366
|
function isValidPackageName(projectName) {
|
|
289
367
|
return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(
|
|
@@ -293,6 +371,7 @@ function isValidPackageName(projectName) {
|
|
|
293
371
|
|
|
294
372
|
/**
|
|
295
373
|
* @param {string} projectName
|
|
374
|
+
* @returns {string}
|
|
296
375
|
*/
|
|
297
376
|
function toValidPackageName(projectName) {
|
|
298
377
|
return projectName
|
|
@@ -306,6 +385,7 @@ function toValidPackageName(projectName) {
|
|
|
306
385
|
/**
|
|
307
386
|
* @param {string} srcDir
|
|
308
387
|
* @param {string} destDir
|
|
388
|
+
* @returns {void}
|
|
309
389
|
*/
|
|
310
390
|
function copyDir(srcDir, destDir) {
|
|
311
391
|
fs.mkdirSync(destDir, { recursive: true });
|
|
@@ -317,42 +397,49 @@ function copyDir(srcDir, destDir) {
|
|
|
317
397
|
}
|
|
318
398
|
|
|
319
399
|
/**
|
|
320
|
-
* @param {string}
|
|
400
|
+
* @param {string} directoryPath
|
|
401
|
+
* @returns {boolean}
|
|
321
402
|
*/
|
|
322
|
-
function isEmpty(
|
|
323
|
-
const files = fs.readdirSync(
|
|
403
|
+
function isEmpty(directoryPath) {
|
|
404
|
+
const files = fs.readdirSync(directoryPath);
|
|
324
405
|
return files.length === 0 || (files.length === 1 && files[0] === '.git');
|
|
325
406
|
}
|
|
326
407
|
|
|
327
408
|
/**
|
|
328
409
|
* @param {string} dir
|
|
410
|
+
* @returns {void}
|
|
329
411
|
*/
|
|
330
412
|
function emptyDir(dir) {
|
|
331
413
|
if (!fs.existsSync(dir)) {
|
|
332
414
|
return;
|
|
333
415
|
}
|
|
416
|
+
|
|
334
417
|
for (const file of fs.readdirSync(dir)) {
|
|
335
418
|
fs.rmSync(path.resolve(dir, file), { recursive: true, force: true });
|
|
336
419
|
}
|
|
337
420
|
}
|
|
338
421
|
|
|
339
422
|
/**
|
|
340
|
-
* @param {string | undefined} userAgent
|
|
341
|
-
* @returns
|
|
423
|
+
* @param {string | undefined} userAgent
|
|
424
|
+
* @returns {UserAgentPackage | undefined}
|
|
342
425
|
*/
|
|
343
426
|
function pkgFromUserAgent(userAgent) {
|
|
344
|
-
if (!userAgent)
|
|
427
|
+
if (!userAgent) {
|
|
428
|
+
return undefined;
|
|
429
|
+
}
|
|
430
|
+
|
|
345
431
|
const pkgSpec = userAgent.split(' ')[0];
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
432
|
+
const [name, version] = pkgSpec.split('/');
|
|
433
|
+
if (!name || !version) {
|
|
434
|
+
return undefined;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return { name, version };
|
|
351
438
|
}
|
|
352
439
|
|
|
353
440
|
/**
|
|
354
441
|
* @param {string} pkgManager
|
|
355
|
-
* @returns string
|
|
442
|
+
* @returns {string}
|
|
356
443
|
*/
|
|
357
444
|
function getInstallCommand(pkgManager) {
|
|
358
445
|
return pkgManager === 'yarn' ? 'yarn' : `${pkgManager} install`;
|
|
@@ -360,46 +447,74 @@ function getInstallCommand(pkgManager) {
|
|
|
360
447
|
|
|
361
448
|
/**
|
|
362
449
|
* @param {string} pkgManager
|
|
363
|
-
* @returns string
|
|
450
|
+
* @returns {string}
|
|
364
451
|
*/
|
|
365
452
|
function getStartCommand(pkgManager) {
|
|
366
453
|
return pkgManager === 'yarn' ? 'yarn dev' : `${pkgManager} run dev`;
|
|
367
454
|
}
|
|
368
455
|
|
|
456
|
+
/**
|
|
457
|
+
* @param {(file: string, content?: string) => void} write
|
|
458
|
+
* @param {string} filesDir
|
|
459
|
+
* @returns {void}
|
|
460
|
+
*/
|
|
369
461
|
function addTailwindDirectives(write, filesDir) {
|
|
370
462
|
write(
|
|
371
463
|
'src/styles.css',
|
|
372
|
-
fs.readFileSync(path.join(filesDir,
|
|
464
|
+
fs.readFileSync(path.join(filesDir, 'styles.css'), 'utf-8'),
|
|
373
465
|
);
|
|
374
466
|
}
|
|
375
467
|
|
|
468
|
+
/**
|
|
469
|
+
* @param {PackageJson} pkg
|
|
470
|
+
* @returns {void}
|
|
471
|
+
*/
|
|
376
472
|
function addTailwindDependencies(pkg) {
|
|
377
|
-
pkg.devDependencies
|
|
378
|
-
pkg.devDependencies
|
|
473
|
+
pkg.devDependencies ??= {};
|
|
474
|
+
pkg.devDependencies.tailwindcss = '^4.2.2';
|
|
379
475
|
pkg.devDependencies['@tailwindcss/vite'] = '^4.2.2';
|
|
380
476
|
}
|
|
381
477
|
|
|
478
|
+
/**
|
|
479
|
+
* @param {PackageJson} pkg
|
|
480
|
+
* @param {Template} template
|
|
481
|
+
* @returns {void}
|
|
482
|
+
*/
|
|
382
483
|
function addYarnDevDependencies(pkg, template) {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
pkg.devDependencies
|
|
484
|
+
if (H3_TEMPLATES.includes(template)) {
|
|
485
|
+
pkg.devDependencies ??= {};
|
|
486
|
+
pkg.devDependencies.h3 = '^1.13.0';
|
|
386
487
|
}
|
|
387
488
|
}
|
|
388
489
|
|
|
490
|
+
/**
|
|
491
|
+
* @param {PackageJson} pkg
|
|
492
|
+
* @param {Template} template
|
|
493
|
+
* @returns {void}
|
|
494
|
+
*/
|
|
389
495
|
function addPnpmDependencies(pkg, template) {
|
|
390
|
-
if (template
|
|
391
|
-
pkg.dependencies
|
|
496
|
+
if (H3_TEMPLATES.includes(template)) {
|
|
497
|
+
pkg.dependencies ??= {};
|
|
498
|
+
pkg.dependencies.h3 = '^1.13.0';
|
|
392
499
|
}
|
|
393
500
|
}
|
|
394
501
|
|
|
502
|
+
/**
|
|
503
|
+
* @param {string} root
|
|
504
|
+
* @param {PackageJson} pkg
|
|
505
|
+
* @param {HighlighterId} highlighter
|
|
506
|
+
* @returns {void}
|
|
507
|
+
*/
|
|
395
508
|
function ensureSyntaxHighlighter(root, pkg, highlighter) {
|
|
509
|
+
const config = HIGHLIGHTERS[highlighter];
|
|
510
|
+
|
|
396
511
|
replacePlaceholders(root, 'src/app/app.config.ts', {
|
|
397
|
-
__HIGHLIGHTER__:
|
|
398
|
-
__HIGHLIGHTER_ENTRY_POINT__:
|
|
512
|
+
__HIGHLIGHTER__: config.highlighter,
|
|
513
|
+
__HIGHLIGHTER_ENTRY_POINT__: config.entryPoint,
|
|
399
514
|
});
|
|
400
515
|
|
|
401
|
-
|
|
402
|
-
for (const [name, version] of Object.entries(dependencies)) {
|
|
516
|
+
pkg.dependencies ??= {};
|
|
517
|
+
for (const [name, version] of Object.entries(config.dependencies)) {
|
|
403
518
|
pkg.dependencies[name] = version;
|
|
404
519
|
}
|
|
405
520
|
|
|
@@ -408,44 +523,94 @@ function ensureSyntaxHighlighter(root, pkg, highlighter) {
|
|
|
408
523
|
});
|
|
409
524
|
}
|
|
410
525
|
|
|
526
|
+
/**
|
|
527
|
+
* @param {Record<string, string>} obj
|
|
528
|
+
* @returns {Record<string, string>}
|
|
529
|
+
*/
|
|
411
530
|
function sortObjectKeys(obj) {
|
|
412
531
|
return Object.keys(obj)
|
|
413
532
|
.sort()
|
|
414
533
|
.reduce((result, key) => {
|
|
415
534
|
result[key] = obj[key];
|
|
416
535
|
return result;
|
|
417
|
-
}, {});
|
|
536
|
+
}, /** @type {Record<string, string>} */ ({}));
|
|
418
537
|
}
|
|
419
538
|
|
|
539
|
+
/**
|
|
540
|
+
* @param {string} root
|
|
541
|
+
* @param {string} title
|
|
542
|
+
* @returns {void}
|
|
543
|
+
*/
|
|
420
544
|
function setProjectTitle(root, title) {
|
|
421
545
|
replacePlaceholders(root, ['index.html', 'README.md'], {
|
|
422
546
|
__PROJECT_TITLE__: title,
|
|
423
547
|
});
|
|
424
548
|
}
|
|
425
549
|
|
|
550
|
+
/**
|
|
551
|
+
* @param {string} root
|
|
552
|
+
* @param {string | readonly string[]} files
|
|
553
|
+
* @param {Record<string, string>} config
|
|
554
|
+
* @returns {void}
|
|
555
|
+
*/
|
|
426
556
|
function replacePlaceholders(root, files, config) {
|
|
427
|
-
for (const file of
|
|
557
|
+
for (const file of toArray(files)) {
|
|
428
558
|
const filePath = path.join(root, file);
|
|
429
559
|
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
|
430
560
|
const newFileContent = Object.keys(config).reduce(
|
|
431
561
|
(content, placeholder) =>
|
|
432
|
-
content.
|
|
562
|
+
content.replaceAll(placeholder, config[placeholder]),
|
|
433
563
|
fileContent,
|
|
434
564
|
);
|
|
435
565
|
fs.writeFileSync(filePath, newFileContent);
|
|
436
566
|
}
|
|
437
567
|
}
|
|
438
568
|
|
|
439
|
-
|
|
440
|
-
|
|
569
|
+
/**
|
|
570
|
+
* @param {string | readonly string[] | undefined | null} value
|
|
571
|
+
* @returns {string[]}
|
|
572
|
+
*/
|
|
573
|
+
function toArray(value) {
|
|
574
|
+
if (value == null) {
|
|
575
|
+
return [];
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
return Array.isArray(value) ? [...value] : [/** @type {string} */ (value)];
|
|
441
579
|
}
|
|
442
580
|
|
|
581
|
+
/**
|
|
582
|
+
* @param {unknown} arg
|
|
583
|
+
* @returns {boolean | undefined}
|
|
584
|
+
*/
|
|
443
585
|
function fromBoolArg(arg) {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
586
|
+
if (typeof arg === 'boolean' || typeof arg === 'undefined') {
|
|
587
|
+
return arg;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (typeof arg !== 'string') {
|
|
591
|
+
return undefined;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
return arg === '' || arg === 'true';
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* @param {string | undefined} value
|
|
599
|
+
* @returns {Template | undefined}
|
|
600
|
+
*/
|
|
601
|
+
function resolveTemplate(value) {
|
|
602
|
+
return isTemplate(value) ? value : undefined;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* @param {string | undefined} value
|
|
607
|
+
* @returns {value is Template}
|
|
608
|
+
*/
|
|
609
|
+
function isTemplate(value) {
|
|
610
|
+
return value === 'latest' || value === 'blog' || value === 'minimal';
|
|
447
611
|
}
|
|
448
612
|
|
|
449
|
-
init().catch((
|
|
450
|
-
console.error(
|
|
613
|
+
init().catch((error) => {
|
|
614
|
+
console.error(error);
|
|
615
|
+
process.exitCode = 1;
|
|
451
616
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-analog",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.20",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Brandon Roberts",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
},
|
|
37
37
|
"homepage": "https://github.com/analogjs/analog/tree/main#readme",
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"kolorist": "^1.
|
|
40
|
-
"minimist": "^1.2.
|
|
39
|
+
"kolorist": "^1.8.0",
|
|
40
|
+
"minimist": "^1.2.8",
|
|
41
41
|
"prompts": "^2.4.2"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"test": "ng test"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@analogjs/content": "^3.0.0-alpha.
|
|
19
|
-
"@analogjs/router": "^3.0.0-alpha.
|
|
18
|
+
"@analogjs/content": "^3.0.0-alpha.20",
|
|
19
|
+
"@analogjs/router": "^3.0.0-alpha.20",
|
|
20
20
|
"@angular/animations": "^17.2.0",
|
|
21
21
|
"@angular/common": "^17.2.0",
|
|
22
22
|
"@angular/compiler": "^17.2.0",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"zone.js": "~0.14.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@analogjs/platform": "^3.0.0-alpha.
|
|
42
|
-
"@analogjs/vite-plugin-angular": "^3.0.0-alpha.
|
|
43
|
-
"@analogjs/vitest-angular": "^3.0.0-alpha.
|
|
41
|
+
"@analogjs/platform": "^3.0.0-alpha.20",
|
|
42
|
+
"@analogjs/vite-plugin-angular": "^3.0.0-alpha.20",
|
|
43
|
+
"@analogjs/vitest-angular": "^3.0.0-alpha.20",
|
|
44
44
|
"@angular-devkit/build-angular": "^17.2.0",
|
|
45
45
|
"@angular/cli": "^17.2.0",
|
|
46
46
|
"@angular/compiler-cli": "^17.2.0",
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
},
|
|
16
16
|
"private": true,
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@analogjs/content": "^3.0.0-alpha.
|
|
19
|
-
"@analogjs/router": "^3.0.0-alpha.
|
|
18
|
+
"@analogjs/content": "^3.0.0-alpha.20",
|
|
19
|
+
"@analogjs/router": "^3.0.0-alpha.20",
|
|
20
20
|
"@angular/animations": "^18.0.0",
|
|
21
21
|
"@angular/build": "^18.0.0",
|
|
22
22
|
"@angular/common": "^18.0.0",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"zone.js": "~0.14.3"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@analogjs/platform": "^3.0.0-alpha.
|
|
42
|
-
"@analogjs/vite-plugin-angular": "^3.0.0-alpha.
|
|
43
|
-
"@analogjs/vitest-angular": "^3.0.0-alpha.
|
|
41
|
+
"@analogjs/platform": "^3.0.0-alpha.20",
|
|
42
|
+
"@analogjs/vite-plugin-angular": "^3.0.0-alpha.20",
|
|
43
|
+
"@analogjs/vitest-angular": "^3.0.0-alpha.20",
|
|
44
44
|
"@angular/cli": "^18.0.0",
|
|
45
45
|
"@angular/compiler-cli": "^18.0.0",
|
|
46
46
|
"jsdom": "^22.0.0",
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
},
|
|
16
16
|
"private": true,
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@analogjs/content": "^3.0.0-alpha.
|
|
19
|
-
"@analogjs/router": "^3.0.0-alpha.
|
|
18
|
+
"@analogjs/content": "^3.0.0-alpha.20",
|
|
19
|
+
"@analogjs/router": "^3.0.0-alpha.20",
|
|
20
20
|
"@angular/animations": "^19.0.0",
|
|
21
21
|
"@angular/common": "^19.0.0",
|
|
22
22
|
"@angular/compiler": "^19.0.0",
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
"zone.js": "~0.15.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@analogjs/platform": "^3.0.0-alpha.
|
|
41
|
-
"@analogjs/vite-plugin-angular": "^3.0.0-alpha.
|
|
42
|
-
"@analogjs/vitest-angular": "^3.0.0-alpha.
|
|
40
|
+
"@analogjs/platform": "^3.0.0-alpha.20",
|
|
41
|
+
"@analogjs/vite-plugin-angular": "^3.0.0-alpha.20",
|
|
42
|
+
"@analogjs/vitest-angular": "^3.0.0-alpha.20",
|
|
43
43
|
"@angular-devkit/build-angular": "^19.0.0",
|
|
44
44
|
"@angular/build": "^19.0.0",
|
|
45
45
|
"@angular/cli": "^19.0.0",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
},
|
|
17
17
|
"private": true,
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@analogjs/content": "^3.0.0-alpha.
|
|
20
|
-
"@analogjs/router": "^3.0.0-alpha.
|
|
19
|
+
"@analogjs/content": "^3.0.0-alpha.20",
|
|
20
|
+
"@analogjs/router": "^3.0.0-alpha.20",
|
|
21
21
|
"@angular/animations": "^20.0.0",
|
|
22
22
|
"@angular/common": "^20.0.0",
|
|
23
23
|
"@angular/compiler": "^20.0.0",
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
"zone.js": "~0.15.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@analogjs/platform": "^3.0.0-alpha.
|
|
41
|
-
"@analogjs/vite-plugin-angular": "^3.0.0-alpha.
|
|
42
|
-
"@analogjs/vitest-angular": "^3.0.0-alpha.
|
|
40
|
+
"@analogjs/platform": "^3.0.0-alpha.20",
|
|
41
|
+
"@analogjs/vite-plugin-angular": "^3.0.0-alpha.20",
|
|
42
|
+
"@analogjs/vitest-angular": "^3.0.0-alpha.20",
|
|
43
43
|
"@angular-devkit/build-angular": "^20.0.0",
|
|
44
44
|
"@angular/build": "^20.0.0",
|
|
45
45
|
"@angular/cli": "^20.0.0",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
},
|
|
17
17
|
"private": true,
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@analogjs/content": "^3.0.0-alpha.
|
|
20
|
-
"@analogjs/router": "^3.0.0-alpha.
|
|
19
|
+
"@analogjs/content": "^3.0.0-alpha.20",
|
|
20
|
+
"@analogjs/router": "^3.0.0-alpha.20",
|
|
21
21
|
"@angular/animations": "^21.0.0",
|
|
22
22
|
"@angular/common": "^21.0.0",
|
|
23
23
|
"@angular/compiler": "^21.0.0",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"tslib": "^2.3.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@analogjs/platform": "^3.0.0-alpha.
|
|
39
|
-
"@analogjs/vite-plugin-angular": "^3.0.0-alpha.
|
|
40
|
-
"@analogjs/vitest-angular": "^3.0.0-alpha.
|
|
38
|
+
"@analogjs/platform": "^3.0.0-alpha.20",
|
|
39
|
+
"@analogjs/vite-plugin-angular": "^3.0.0-alpha.20",
|
|
40
|
+
"@analogjs/vitest-angular": "^3.0.0-alpha.20",
|
|
41
41
|
"@angular/build": "^21.0.0",
|
|
42
42
|
"@angular/cli": "^21.0.0",
|
|
43
43
|
"@angular/compiler-cli": "^21.0.0",
|
|
@@ -8,14 +8,18 @@ import {
|
|
|
8
8
|
provideBrowserGlobalErrorListeners,
|
|
9
9
|
} from '@angular/core';
|
|
10
10
|
import { provideClientHydration, withEventReplay } from '@angular/platform-browser';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
provideFileRouter,
|
|
13
|
+
requestContextInterceptor,
|
|
14
|
+
} from '@analogjs/router';
|
|
15
|
+
import { withContentRoutes } from '@analogjs/router/content';
|
|
12
16
|
import { provideContent, withMarkdownRenderer } from '@analogjs/content';
|
|
13
17
|
import { __HIGHLIGHTER__ } from '@analogjs/content/__HIGHLIGHTER_ENTRY_POINT__';
|
|
14
18
|
|
|
15
19
|
export const appConfig: ApplicationConfig = {
|
|
16
20
|
providers: [
|
|
17
21
|
provideBrowserGlobalErrorListeners(),
|
|
18
|
-
provideFileRouter(),
|
|
22
|
+
provideFileRouter(withContentRoutes()),
|
|
19
23
|
provideHttpClient(
|
|
20
24
|
withFetch(),
|
|
21
25
|
withInterceptors([requestContextInterceptor])
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
},
|
|
17
17
|
"private": true,
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@analogjs/content": "^3.0.0-alpha.
|
|
20
|
-
"@analogjs/router": "^3.0.0-alpha.
|
|
19
|
+
"@analogjs/content": "^3.0.0-alpha.20",
|
|
20
|
+
"@analogjs/router": "^3.0.0-alpha.20",
|
|
21
21
|
"@angular/animations": "^21.0.0",
|
|
22
22
|
"@angular/common": "^21.0.0",
|
|
23
23
|
"@angular/compiler": "^21.0.0",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"tslib": "^2.3.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@analogjs/platform": "^3.0.0-alpha.
|
|
40
|
-
"@analogjs/vite-plugin-angular": "^3.0.0-alpha.
|
|
41
|
-
"@analogjs/vitest-angular": "^3.0.0-alpha.
|
|
39
|
+
"@analogjs/platform": "^3.0.0-alpha.20",
|
|
40
|
+
"@analogjs/vite-plugin-angular": "^3.0.0-alpha.20",
|
|
41
|
+
"@analogjs/vitest-angular": "^3.0.0-alpha.20",
|
|
42
42
|
"@angular/build": "^21.0.0",
|
|
43
43
|
"@angular/cli": "^21.0.0",
|
|
44
44
|
"@angular/compiler-cli": "^21.0.0",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
},
|
|
17
17
|
"private": true,
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@analogjs/content": "^3.0.0-alpha.
|
|
20
|
-
"@analogjs/router": "^3.0.0-alpha.
|
|
19
|
+
"@analogjs/content": "^3.0.0-alpha.20",
|
|
20
|
+
"@analogjs/router": "^3.0.0-alpha.20",
|
|
21
21
|
"@angular/animations": "^21.0.0",
|
|
22
22
|
"@angular/common": "^21.0.0",
|
|
23
23
|
"@angular/compiler": "^21.0.0",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"tslib": "^2.3.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@analogjs/platform": "^3.0.0-alpha.
|
|
40
|
-
"@analogjs/vite-plugin-angular": "^3.0.0-alpha.
|
|
41
|
-
"@analogjs/vitest-angular": "^3.0.0-alpha.
|
|
39
|
+
"@analogjs/platform": "^3.0.0-alpha.20",
|
|
40
|
+
"@analogjs/vite-plugin-angular": "^3.0.0-alpha.20",
|
|
41
|
+
"@analogjs/vitest-angular": "^3.0.0-alpha.20",
|
|
42
42
|
"@angular/build": "^21.0.0",
|
|
43
43
|
"@angular/cli": "^21.0.0",
|
|
44
44
|
"@angular/compiler-cli": "^21.0.0",
|