Skip to content

Commit df24f72

Browse files
committed
chore: first pass at remote handling
1 parent d9a994d commit df24f72

File tree

6 files changed

+194
-140
lines changed

6 files changed

+194
-140
lines changed

src/add-ons.ts

Lines changed: 43 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,26 @@
11
import { readFile } from 'node:fs/promises'
2-
import { existsSync, readdirSync, statSync } from 'node:fs'
2+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'
33
import { resolve } from 'node:path'
44
import { fileURLToPath } from 'node:url'
55
import chalk from 'chalk'
66

77
import { DEFAULT_FRAMEWORK } from './constants.js'
8-
import type { CliOptions, Framework } from './types.js'
8+
import type { AddOn, CliOptions, Framework } from './types.js'
99

10-
type BooleanVariable = {
11-
name: string
12-
default: boolean
13-
description: string
14-
type: 'boolean'
15-
}
16-
17-
type NumberVariable = {
18-
name: string
19-
default: number
20-
description: string
21-
type: 'number'
22-
}
23-
24-
type StringVariable = {
25-
name: string
26-
default: string
27-
description: string
28-
type: 'string'
10+
function isDirectory(path: string): boolean {
11+
return statSync(path).isDirectory()
2912
}
3013

31-
export type Variable = BooleanVariable | NumberVariable | StringVariable
32-
33-
export type AddOn = {
34-
id: string
35-
type: 'add-on' | 'example'
36-
name: string
37-
description: string
38-
link: string
39-
templates: Array<string>
40-
routes: Array<{
41-
url: string
42-
name: string
43-
}>
44-
directory: string
45-
packageAdditions: {
46-
dependencies?: Record<string, string>
47-
devDependencies?: Record<string, string>
48-
scripts?: Record<string, string>
49-
}
50-
command?: {
51-
command: string
52-
args?: Array<string>
14+
function findFilesRecursively(path: string, files: Record<string, string>) {
15+
const dirFiles = readdirSync(path)
16+
for (const file of dirFiles) {
17+
const filePath = resolve(path, file)
18+
if (isDirectory(filePath)) {
19+
findFilesRecursively(filePath, files)
20+
} else {
21+
files[filePath] = readFileSync(filePath, 'utf-8').toString()
22+
}
5323
}
54-
readme?: string
55-
phase: 'setup' | 'add-on'
56-
shadcnComponents?: Array<string>
57-
warning?: string
58-
dependsOn?: Array<string>
59-
variables?: Array<Variable>
60-
}
61-
62-
function isDirectory(path: string): boolean {
63-
return statSync(path).isDirectory()
6424
}
6525

6626
export async function getAllAddOns(
@@ -101,13 +61,24 @@ export async function getAllAddOns(
10161
readme = await readFile(resolve(addOnsBase, dir, 'README.md'), 'utf-8')
10262
}
10363

64+
const absoluteFiles: Record<string, string> = {}
65+
const assetsDir = resolve(addOnsBase, dir, 'assets')
66+
if (existsSync(assetsDir)) {
67+
await findFilesRecursively(assetsDir, absoluteFiles)
68+
}
69+
const files: Record<string, string> = {}
70+
for (const file of Object.keys(absoluteFiles)) {
71+
files[file.replace(assetsDir, '.')] = absoluteFiles[file]
72+
}
73+
10474
addOns.push({
10575
...info,
10676
id: dir,
10777
type,
108-
directory: resolve(addOnsBase, dir),
10978
packageAdditions,
11079
readme,
80+
files,
81+
deletedFiles: [],
11182
})
11283
}
11384
}
@@ -126,8 +97,13 @@ export async function finalizeAddOns(
12697
const addOns = await getAllAddOns(framework, template)
12798

12899
for (const addOnID of finalAddOnIDs) {
129-
const addOn = addOns.find((a) => a.id === addOnID)
130-
if (!addOn) {
100+
let addOn: AddOn | undefined
101+
const localAddOn = addOns.find((a) => a.id === addOnID)
102+
if (localAddOn) {
103+
addOn = localAddOn
104+
} else if (addOnID.startsWith('http')) {
105+
addOn = await loadRemoteAddOn(addOnID)
106+
} else {
131107
throw new Error(`Add-on ${addOnID} not found`)
132108
}
133109

@@ -154,3 +130,14 @@ export async function listAddOns(options: CliOptions) {
154130
console.log(`${chalk.bold(addOn.id)}: ${addOn.description}`)
155131
}
156132
}
133+
134+
function loadAddOn(path: string): AddOn {
135+
const fileContent = await readFile(path, 'utf-8')
136+
return JSON.parse(fileContent)
137+
}
138+
139+
async function loadRemoteAddOn(url: string): AddOn {
140+
const response = await fetch(url)
141+
const fileContent = await response.json()
142+
return JSON.parse(fileContent)
143+
}

src/create-app.ts

Lines changed: 53 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ import { sortObject } from './utils.js'
1010
import { writeConfigFile } from './config-file.js'
1111
import { packageManagerExecute } from './package-manager.js'
1212

13-
import type { Environment } from './environment.js'
14-
import type { Options } from './types.js'
13+
import type { Environment, Options } from './types.js'
1514

1615
function createCopyFiles(environment: Environment, targetDir: string) {
1716
return async function copyFiles(
@@ -48,8 +47,8 @@ function createTemplateFile(
4847
targetDir: string,
4948
) {
5049
return async function templateFile(
51-
templateDir: string,
5250
file: string,
51+
content: string,
5352
targetFileName?: string,
5453
extraTemplateValues?: Record<string, any>,
5554
) {
@@ -116,13 +115,8 @@ function createTemplateFile(
116115
getPackageManagerRunScript,
117116
}
118117

119-
const template = await environment.readFile(
120-
resolve(templateDir, file),
121-
'utf-8',
122-
)
123-
let content = ''
124118
try {
125-
content = render(template, templateValues)
119+
content = render(content, templateValues)
126120
} catch (error) {
127121
console.error(chalk.red(`EJS error in file ${file}`))
128122
console.error(error)
@@ -253,50 +247,33 @@ async function createPackageJSON(
253247
)
254248
}
255249

256-
async function copyFilesRecursively(
250+
async function copyAddOnFile(
257251
environment: Environment,
258-
source: string,
252+
content: string,
259253
target: string,
260-
templateFile: (file: string, targetFileName?: string) => Promise<void>,
254+
templateFile: (content: string, targetFileName: string) => Promise<void>,
261255
) {
262-
if (environment.isDirectory(source)) {
263-
const files = environment.readdir(source)
264-
for (const file of files) {
265-
const sourceChild = resolve(source, file)
266-
const targetChild = resolve(target, file)
267-
await copyFilesRecursively(
268-
environment,
269-
sourceChild,
270-
targetChild,
271-
templateFile,
272-
)
273-
}
274-
} else {
275-
let targetFile = basename(target).replace(/_dot_/, '.')
276-
let isTemplate = false
277-
if (targetFile.endsWith('.ejs')) {
278-
targetFile = targetFile.replace('.ejs', '')
279-
isTemplate = true
280-
}
281-
let isAppend = false
282-
if (targetFile.endsWith('.append')) {
283-
targetFile = targetFile.replace('.append', '')
284-
isAppend = true
285-
}
256+
let targetFile = basename(target).replace(/_dot_/, '.')
257+
let isTemplate = false
258+
if (targetFile.endsWith('.ejs')) {
259+
targetFile = targetFile.replace('.ejs', '')
260+
isTemplate = true
261+
}
262+
let isAppend = false
263+
if (targetFile.endsWith('.append')) {
264+
targetFile = targetFile.replace('.append', '')
265+
isAppend = true
266+
}
286267

287-
const targetPath = resolve(dirname(target), targetFile)
268+
const targetPath = resolve(dirname(target), targetFile)
288269

289-
if (isTemplate) {
290-
await templateFile(source, targetPath)
270+
if (isTemplate) {
271+
await templateFile(content, targetPath)
272+
} else {
273+
if (isAppend) {
274+
await environment.appendFile(targetPath, content)
291275
} else {
292-
if (isAppend) {
293-
await environment.appendFile(
294-
targetPath,
295-
(await environment.readFile(source)).toString(),
296-
)
297-
} else {
298-
await environment.copyFile(source, targetPath)
299-
}
276+
await environment.writeFile(targetPath, content)
300277
}
301278
}
302279
}
@@ -338,13 +315,31 @@ export async function createApp(
338315
}
339316

340317
const copyFiles = createCopyFiles(environment, targetDir)
341-
const templateFile = createTemplateFile(
318+
const templateFileFromContent = createTemplateFile(
342319
environment,
343320
options.projectName,
344321
options,
345322
targetDir,
346323
)
347324

325+
async function templateFile(
326+
templateBase: string,
327+
file: string,
328+
targetFileName?: string,
329+
extraTemplateValues?: Record<string, any>,
330+
) {
331+
const content = await environment.readFile(
332+
resolve(templateBase, file),
333+
'utf-8',
334+
)
335+
return templateFileFromContent(
336+
file,
337+
content.toString(),
338+
targetFileName,
339+
extraTemplateValues,
340+
)
341+
}
342+
348343
const isAddOnEnabled = (id: string) =>
349344
options.chosenAddOns.find((a) => a.id === id)
350345

@@ -448,15 +443,16 @@ export async function createApp(
448443
(addOn) => addOn.phase === phase,
449444
)) {
450445
s?.start(`Setting up ${addOn.name}...`)
451-
const addOnDir = resolve(addOn.directory, 'assets')
452-
if (environment.exists(addOnDir)) {
453-
await copyFilesRecursively(
454-
environment,
455-
addOnDir,
456-
targetDir,
457-
async (file: string, targetFileName?: string) =>
458-
templateFile(addOnDir, file, targetFileName),
459-
)
446+
if (addOn.files) {
447+
for (const file of Object.keys(addOn.files)) {
448+
copyAddOnFile(
449+
environment,
450+
addOn.files[file],
451+
file,
452+
(content, targetFileName) =>
453+
templateFileFromContent(targetFileName, content),
454+
)
455+
}
460456
}
461457

462458
if (addOn.command) {

src/custom-add-on.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,15 @@ This is probably because this was created with an older version of create-tsrout
9090
description: 'A custom add-on',
9191
author: 'John Doe',
9292
license: 'MIT',
93+
link: 'https://github.com/john-doe/custom-add-on',
9394
commands: [],
95+
shadcnComponents: [],
96+
templates: [persistedOptions.mode],
97+
routes: [],
98+
warning: '',
99+
variables: {},
100+
type: 'example',
101+
phase: 'add-on',
94102
},
95103
null,
96104
2,
@@ -111,11 +119,17 @@ This is probably because this was created with an older version of create-tsrout
111119
(await readFile('package.json')).toString(),
112120
)
113121

122+
info.packageAdditions = {
123+
scripts: {},
124+
dependencies: {},
125+
devDependencies: {},
126+
}
127+
114128
if (
115129
JSON.stringify(originalPackageJson.scripts) !==
116130
JSON.stringify(currentPackageJson.scripts)
117131
) {
118-
info.commands.push('start')
132+
info.packageAdditions.scripts = currentPackageJson.scripts
119133
}
120134

121135
const dependencies: Record<string, string> = {}
@@ -127,7 +141,7 @@ This is probably because this was created with an older version of create-tsrout
127141
dependencies[dependency] = currentPackageJson.dependencies[dependency]
128142
}
129143
}
130-
info.dependencies = dependencies
144+
info.packageAdditions.dependencies = dependencies
131145

132146
const devDependencies: Record<string, string> = {}
133147
for (const dependency of Object.keys(currentPackageJson.devDependencies)) {
@@ -139,7 +153,7 @@ This is probably because this was created with an older version of create-tsrout
139153
currentPackageJson.devDependencies[dependency]
140154
}
141155
}
142-
info.devDependencies = devDependencies
156+
info.packageAdditions.devDependencies = devDependencies
143157

144158
const changedFiles: Record<string, string> = {}
145159
await compareFiles('.', originalOutput.files, changedFiles)

src/environment.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,7 @@ import { dirname } from 'node:path'
1010
import { execa } from 'execa'
1111
import { memfs } from 'memfs'
1212

13-
export type Environment = {
14-
startRun: () => void
15-
finishRun: () => void
16-
getErrors: () => Array<string>
17-
18-
appendFile: (path: string, contents: string) => Promise<void>
19-
copyFile: (from: string, to: string) => Promise<void>
20-
writeFile: (path: string, contents: string) => Promise<void>
21-
execute: (command: string, args: Array<string>, cwd: string) => Promise<void>
22-
23-
readFile: (path: string, encoding?: BufferEncoding) => Promise<string>
24-
exists: (path: string) => boolean
25-
readdir: (path: string) => Array<string>
26-
isDirectory: (path: string) => boolean
27-
}
13+
import type { Environment } from './types.js'
2814

2915
export function createDefaultEnvironment(): Environment {
3016
let errors: Array<string> = []

0 commit comments

Comments
 (0)