Skip to content

Commit 816e10c

Browse files
committed
Improvements and fixes
1 parent 9922174 commit 816e10c

File tree

18 files changed

+607
-387
lines changed

18 files changed

+607
-387
lines changed

.lefthook.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1-
---
21
assert_lefthook_installed: true
2+
pre-commit:
3+
commands:
4+
deno-lint:
5+
run: deno lint

.mise.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[tools]
22
node = "23.6.1"
3-
deno = "2.2.4"
3+
deno = "2.4.5"

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"window.title": "${folderName} ${separator}${activeRepositoryBranchName}${separator}${remoteName} (python)",
23
"files.exclude": {
34
"**/.git": true,
45
"**/.data": true,

commands/install.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ async function renderMainScreen(char: string) {
167167
process.stdout.write(ansiEscapes.cursorRestorePosition)
168168
process.stdout.write(ansiEscapes.cursorShow)
169169
process.stdout.write(ansiEscapes.exitAlternativeScreen)
170-
process.exit()
170+
Deno.exit()
171171
} else if (char === 'j') {
172172
const idx = Projects.findIndex((project) => project.name === Ctx.currentProject)
173173
const newIdx = Math.min(Projects.length - 1, idx + 1)
@@ -339,7 +339,7 @@ async function renderUpdateVersionScreen(char: string) {
339339
}
340340

341341
if (char === '\x1B' || char === 'q') {
342-
process.exit()
342+
Deno.exit()
343343
} else if (char === 'j') {
344344
currentVersion = Math.min(project.data.versions.length - 1, currentVersion + 1)
345345
} else if (char === 'k') {

commands/lint.ts

Lines changed: 42 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import toml from 'smol-toml'
1010
import { execa } from 'execa'
1111
import { globby } from 'globby'
1212
import ansiEscapes from 'ansi-escapes'
13+
import * as jsonc from 'jsonc-parser'
14+
import { xdgConfig } from 'xdg-basedir'
15+
import { getEcosystems } from '../devutils/index.ts'
1316

1417
import type { CommandFixOptions, Config, Issue, Project } from '#types'
1518
import type { PackageJson } from 'type-fest'
@@ -64,6 +67,35 @@ export async function run(options: CommandFixOptions, positionals: string[]) {
6467
return
6568
}
6669

70+
// FIX EDITOR SETTINGS
71+
if (await fileExists('.vscode/settings.json')) {
72+
const userSettingsJsonFile = path.join(xdgConfig, 'Code/User/settings.json')
73+
const defaultTitle =
74+
jsonc.parse(await fs.readFile(userSettingsJsonFile, 'utf-8'))['window.title']
75+
76+
const settingsText = await fs.readFile('.vscode/settings.json', 'utf-8')
77+
const settingsJson = jsonc.parse(settingsText)
78+
const ecosystem = (await getEcosystems(Deno.cwd()))[0]
79+
if (settingsJson['window.title']) {
80+
const output = settingsText.replace(
81+
/"window\.title"[ \t]*:[ \t]*".*?"/m,
82+
`"window.title": "${defaultTitle} (${ecosystem})"`,
83+
)
84+
await fs.writeFile('.vscode/settings.json', output)
85+
} else if (Object.keys(settingsJson).length === 0) {
86+
const output = `{
87+
"window.title": "${defaultTitle} (${ecosystem})"
88+
}`
89+
await fs.writeFile('.vscode/settings.json', output)
90+
} else {
91+
const output = settingsText.replace(
92+
/[ \t]*{/m,
93+
`{\n\t"window.title": "${defaultTitle} (${ecosystem})",`,
94+
)
95+
await fs.writeFile('.vscode/settings.json', output)
96+
}
97+
}
98+
6799
const ruleFiles: string[] = []
68100
const collect = async (pattern: string) => {
69101
const matches = await globby(pattern, {
@@ -255,34 +287,31 @@ async function fixFromFile(
255287
if (shouldRunFix) {
256288
await issue.fix()
257289
} else {
258-
printWithTips(`[${styleText('red', 'FAIL')}] ${fixId}`, [
259-
'Failed because the fix function was not executed',
260-
])
261-
failed = true
262-
break
290+
console.info(`[${styleText('yellow', 'SKIP')}] ${fixId}`)
291+
return
263292
}
264293
}
265294

266295
if (!failed) {
267296
console.info(`[${styleText('green', 'PASS')}] ${fixId}`)
268297
} else {
269-
process.exit(1)
298+
Deno.exit(1)
270299
}
271300
} catch (err) {
272301
printWithTips(`[${styleText('red', 'FAIL')}] ${fixId}`, [
273302
'Failed because an error was caught',
274303
])
275304
console.error(err)
276-
process.exit(1)
305+
Deno.exit(1)
277306
}
278307
}
279308

280309
async function getProject(): Promise<Project> {
281310
if (!(await fileExists('.git'))) {
282311
return {
283312
type: 'only-directory',
284-
rootDir: process.cwd(),
285-
name: path.basename(process.cwd()),
313+
rootDir: Deno.cwd(),
314+
name: path.basename(Deno.cwd()),
286315
}
287316
}
288317

@@ -321,8 +350,8 @@ async function getProject(): Promise<Project> {
321350
if (!remoteName) {
322351
return {
323352
type: 'under-version-control',
324-
rootDir: process.cwd(),
325-
name: path.basename(process.cwd()),
353+
rootDir: Deno.cwd(),
354+
name: path.basename(Deno.cwd()),
326355
branchName,
327356
}
328357
}
@@ -337,12 +366,12 @@ async function getProject(): Promise<Project> {
337366
`Failed to extract repository name and owner for remote name "${remoteName}"`,
338367
[`Remote name has URL of "${remoteUrl}"`],
339368
)
340-
process.exit(1)
369+
Deno.exit(1)
341370
}
342371

343372
return {
344373
type: 'with-remote-url',
345-
rootDir: process.cwd(),
374+
rootDir: Deno.cwd(),
346375
name: match.groups.name,
347376
branchName,
348377
remoteName,
@@ -351,64 +380,6 @@ async function getProject(): Promise<Project> {
351380
}
352381
}
353382

354-
export async function getEcosystems(rootDir: string): Promise<string[]> {
355-
using _ = ((origDir: string) => ({
356-
[Symbol.dispose]: () => Deno.chdir(origDir),
357-
}))(Deno.cwd())
358-
Deno.chdir(rootDir)
359-
360-
const ecosystems: string[] = []
361-
362-
if (await fileExists('package.json')) {
363-
ecosystems.push('nodejs')
364-
365-
const content: PackageJson = JSON.parse(
366-
await fs.readFile('package.json', 'utf-8'),
367-
)
368-
if (content.displayName) {
369-
ecosystems.push('vscode-extension')
370-
}
371-
}
372-
373-
if ((await fileExists('deno.jsonc')) || (await fileExists('deno.json'))) {
374-
ecosystems.push('deno')
375-
}
376-
377-
if ((await globby('*.c')).length > 0) {
378-
ecosystems.push('c')
379-
}
380-
381-
// https://cmake.org/cmake/help/latest/command/project.html
382-
if (await fileExists('CMakeLists.txt')) {
383-
const content = await fs.readFile('CMakeLists.txt', 'utf-8')
384-
const language = content.match(
385-
/project\((?:.*? (?<lang>[a-zA-Z]+)\)|.*?LANGUAGES[ \t]+(?<lang>[a-zA-Z]+))/,
386-
)
387-
if (language?.groups?.lang === 'C') {
388-
ecosystems.push('c')
389-
} else if (language?.groups?.lang === 'CXX') {
390-
ecosystems.push('cpp')
391-
} else {
392-
// TODO
393-
console.error(
394-
`CMAkeLists.txt should have language defined in project()`,
395-
)
396-
process.exit(1)
397-
}
398-
}
399-
400-
if (await fileExists('basalt.toml')) {
401-
ecosystems.push('bash')
402-
}
403-
404-
// https://zed.dev/docs/extensions/developing-extensions
405-
if (await fileExists('extension.toml')) {
406-
ecosystems.push('zed-extension')
407-
}
408-
409-
return ecosystems
410-
}
411-
412383
function printWithTips(str: string, tips: string[]) {
413384
console.info(str)
414385
for (const tip of tips) {

commands/new.ts

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,34 @@
11
import * as fs from 'node:fs/promises'
22
import * as path from 'node:path'
3-
import * as util from 'node:util'
4-
import { existsSync } from 'node:fs'
53

6-
import * as ejs from 'ejs'
7-
import { globby } from 'globby'
84
import { execa } from 'execa'
95
import Handlebars from 'handlebars'
106
import { fileExists } from '#common'
117
import * as inquirer from '@inquirer/prompts'
128

139
import type { CommandNewOptions } from '#types'
10+
import { existsSync } from 'node:fs'
11+
12+
const _dirname = import.meta.dirname
1413

1514
export async function run(values: CommandNewOptions, positionals: string[]) {
15+
if (!positionals[0]) {
16+
const input = await inquirer.input({
17+
message: 'Choose a directory',
18+
})
19+
positionals = [input]
20+
}
21+
22+
if (!existsSync(positionals[0])) {
23+
const input = await inquirer.confirm({
24+
message: 'Would you like to create the directory?',
25+
})
26+
27+
if (input) {
28+
await fs.mkdir(positionals[0], { recursive: true })
29+
}
30+
}
31+
1632
if (!values.ecosystem) {
1733
const input = await inquirer.select({
1834
message: 'Choose an ecosystem',
@@ -35,7 +51,7 @@ export async function run(values: CommandNewOptions, positionals: string[]) {
3551
}
3652

3753
const value = await inquirer.select({
38-
message: `Choose a "${values.ecosystem}" template`,
54+
message: `Choose a template`,
3955
choices: Object.entries(parameters).map(([id, { name }]) => ({
4056
name,
4157
value: id,
@@ -58,7 +74,7 @@ export async function run(values: CommandNewOptions, positionals: string[]) {
5874
ecosystem: values.ecosystem,
5975
templateName: values.templateName,
6076
projectName: values.projectName,
61-
forceTemplate: values.force,
77+
forceTemplate: values.force ?? false,
6278
options: (values.options ?? '').split(','),
6379
})
6480
}
@@ -73,7 +89,9 @@ type Context = Readonly<{
7389
}>
7490

7591
export async function createProject(ctx: Context) {
76-
const outputDir = path.resolve(process.cwd(), ctx.dir)
92+
if (!_dirname) throw new TypeError('Variable "import.meta.dirname" is not truthy')
93+
94+
const outputDir = path.resolve(Deno.cwd(), ctx.dir)
7795

7896
if (!(await fileExists(outputDir))) {
7997
await fs.mkdir(outputDir, { recursive: true })
@@ -82,12 +100,12 @@ export async function createProject(ctx: Context) {
82100
if (!ctx.forceTemplate) {
83101
if ((await fs.readdir(outputDir)).length > 0) {
84102
console.error(`Error: Directory must be empty: "${outputDir}"`)
85-
process.exit(1)
103+
Deno.exit(1)
86104
}
87105
}
88106

89107
const templateDir = path.join(
90-
import.meta.dirname,
108+
_dirname,
91109
'../config/templates',
92110
ctx.ecosystem,
93111
`${ctx.ecosystem}-${ctx.templateName}`,
@@ -107,16 +125,24 @@ export async function createProject(ctx: Context) {
107125
const rel = inputFile.slice(templateDir.length + 1)
108126
const outputFile = path.join(outputDir, rel)
109127

110-
let outputContent = ''
111-
{
128+
try {
112129
const template = await fs.readFile(inputFile, 'utf-8')
113130
const runtimeTemplate = Handlebars.compile(template)
114-
outputContent = runtimeTemplate({
131+
const outputContent = runtimeTemplate({
115132
key: 'value',
133+
}, {
134+
helpers: {
135+
'raw': function (options: any) {
136+
return options.fn()
137+
},
138+
},
116139
})
140+
await fs.mkdir(path.dirname(outputFile), { recursive: true })
141+
await fs.writeFile(outputFile, outputContent)
142+
} catch (err) {
143+
console.error(`Failed to template file "${inputFile}"`)
144+
throw err
117145
}
118-
await fs.mkdir(path.dirname(outputFile), { recursive: true })
119-
await fs.writeFile(outputFile, outputContent)
120146
}
121147
}
122148
}
@@ -137,8 +163,8 @@ function getTemplateData(): Record<
137163
cli: {
138164
name: 'CLI',
139165
},
140-
'web-server': {
141-
name: 'Web Server',
166+
'web-server-express': {
167+
name: 'Web Server (Express)',
142168
},
143169
},
144170
async onRun(ctx) {
@@ -199,11 +225,6 @@ function getTemplateData(): Record<
199225
name: 'Playground',
200226
},
201227
},
202-
async onRun(ctx) {
203-
await execa('c++', ['run', '.'], {
204-
stdio: 'inherit',
205-
})
206-
},
207228
},
208229
}
209230
}

0 commit comments

Comments
 (0)