Skip to content

Commit d9a994d

Browse files
committed
chore: merging and adding add-on creation
1 parent 6a6d785 commit d9a994d

File tree

2 files changed

+171
-0
lines changed

2 files changed

+171
-0
lines changed

src/cli.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { SUPPORTED_TOOLCHAINS } from './toolchain.js'
99
import runServer from './mcp.js'
1010
import { listAddOns } from './add-ons.js'
1111
import { DEFAULT_FRAMEWORK, SUPPORTED_FRAMEWORKS } from './constants.js'
12+
import { initAddOn } from './custom-add-on.js'
1213

1314
import { createDefaultEnvironment } from './environment.js'
1415
import { add } from './add.js'
@@ -31,6 +32,13 @@ export function cli() {
3132
await add(addOn.split(',').map((addon) => addon.trim()))
3233
})
3334

35+
program
36+
.command('init-add-on')
37+
.description('Initialize a new add-on from the current project')
38+
.action(async () => {
39+
await initAddOn()
40+
})
41+
3442
program // 104 22
3543
.argument('[project-name]', 'name of the project')
3644
.option('--no-git', 'do not create a git repository')

src/custom-add-on.ts

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import { readFile, readdir } from 'node:fs/promises'
2+
import { existsSync, writeFileSync } from 'node:fs'
3+
import { resolve } from 'node:path'
4+
import chalk from 'chalk'
5+
6+
import { createMemoryEnvironment } from './environment.js'
7+
import { createApp } from './create-app.js'
8+
import { readConfigFile } from './config-file.js'
9+
import { finalizeAddOns } from './add-ons.js'
10+
11+
import type { Options } from './types.js'
12+
import type { PersistedOptions } from './config-file.js'
13+
14+
async function createOptions(
15+
json: PersistedOptions,
16+
): Promise<Required<Options>> {
17+
return {
18+
...json,
19+
chosenAddOns: await finalizeAddOns(json.framework!, json.mode!, [
20+
...json.existingAddOns,
21+
]),
22+
} as Required<Options>
23+
}
24+
25+
async function runCreateApp(options: Required<Options>) {
26+
const { environment, output } = createMemoryEnvironment()
27+
await createApp(options, {
28+
silent: true,
29+
environment,
30+
cwd: process.cwd(),
31+
})
32+
return output
33+
}
34+
35+
const IGNORE_FILES = [
36+
'node_modules',
37+
'dist',
38+
'build',
39+
'.add-ons',
40+
'.git',
41+
'pnpm-lock.yaml',
42+
'package-lock.json',
43+
'yarn.lock',
44+
'bun.lockb',
45+
'bun.lock',
46+
'deno.lock',
47+
'add-on.json',
48+
'add-on-info.json',
49+
'package.json',
50+
]
51+
52+
async function compareFiles(
53+
path: string,
54+
original: Record<string, string>,
55+
changedFiles: Record<string, string>,
56+
) {
57+
const files = await readdir(path, { withFileTypes: true })
58+
for (const file of files) {
59+
const filePath = `${path}/${file.name}`
60+
if (!IGNORE_FILES.includes(file.name)) {
61+
if (file.isDirectory()) {
62+
await compareFiles(filePath, original, changedFiles)
63+
} else {
64+
const contents = (await readFile(filePath)).toString()
65+
const absolutePath = resolve(process.cwd(), filePath)
66+
if (!original[absolutePath] || original[absolutePath] !== contents) {
67+
changedFiles[filePath] = contents
68+
}
69+
}
70+
}
71+
}
72+
}
73+
74+
export async function initAddOn() {
75+
const persistedOptions = await readConfigFile(process.cwd())
76+
if (!persistedOptions) {
77+
console.error(`${chalk.red('There is no .cta.json file in your project.')}
78+
79+
This is probably because this was created with an older version of create-tsrouter-app.`)
80+
return
81+
}
82+
83+
if (!existsSync('add-on-info.json')) {
84+
writeFileSync(
85+
'add-on-info.json',
86+
JSON.stringify(
87+
{
88+
name: 'custom-add-on',
89+
version: '0.0.1',
90+
description: 'A custom add-on',
91+
author: 'John Doe',
92+
license: 'MIT',
93+
commands: [],
94+
},
95+
null,
96+
2,
97+
),
98+
)
99+
}
100+
101+
const info = JSON.parse((await readFile('add-on-info.json')).toString())
102+
103+
const originalOutput = await runCreateApp(
104+
await createOptions(persistedOptions),
105+
)
106+
107+
const originalPackageJson = JSON.parse(
108+
originalOutput.files[resolve(process.cwd(), 'package.json')],
109+
)
110+
const currentPackageJson = JSON.parse(
111+
(await readFile('package.json')).toString(),
112+
)
113+
114+
if (
115+
JSON.stringify(originalPackageJson.scripts) !==
116+
JSON.stringify(currentPackageJson.scripts)
117+
) {
118+
info.commands.push('start')
119+
}
120+
121+
const dependencies: Record<string, string> = {}
122+
for (const dependency of Object.keys(currentPackageJson.dependencies)) {
123+
if (
124+
originalPackageJson.dependencies[dependency] !==
125+
currentPackageJson.dependencies[dependency]
126+
) {
127+
dependencies[dependency] = currentPackageJson.dependencies[dependency]
128+
}
129+
}
130+
info.dependencies = dependencies
131+
132+
const devDependencies: Record<string, string> = {}
133+
for (const dependency of Object.keys(currentPackageJson.devDependencies)) {
134+
if (
135+
originalPackageJson.devDependencies[dependency] !==
136+
currentPackageJson.devDependencies[dependency]
137+
) {
138+
devDependencies[dependency] =
139+
currentPackageJson.devDependencies[dependency]
140+
}
141+
}
142+
info.devDependencies = devDependencies
143+
144+
const changedFiles: Record<string, string> = {}
145+
await compareFiles('.', originalOutput.files, changedFiles)
146+
147+
info.files = changedFiles
148+
info.deletedFiles = []
149+
150+
info.mode = persistedOptions.mode
151+
info.framework = persistedOptions.framework
152+
info.typescript = persistedOptions.typescript
153+
info.tailwind = persistedOptions.tailwind
154+
info.addDependencies = persistedOptions.existingAddOns
155+
156+
for (const file of Object.keys(originalOutput.files)) {
157+
if (!existsSync(file)) {
158+
info.deletedFiles.push(file.replace(process.cwd(), '.'))
159+
}
160+
}
161+
162+
writeFileSync('add-on.json', JSON.stringify(info, null, 2))
163+
}

0 commit comments

Comments
 (0)