@tanstack/cta-engine 0.10.0-alpha.19 → 0.10.0-alpha.21

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.
Files changed (69) hide show
  1. package/dist/add-ons.js +5 -14
  2. package/dist/add-to-app.js +118 -74
  3. package/dist/config-file.js +9 -7
  4. package/dist/create-app.js +112 -34
  5. package/dist/custom-add-ons/add-on.js +175 -0
  6. package/dist/custom-add-ons/shared.js +117 -0
  7. package/dist/custom-add-ons/starter.js +84 -0
  8. package/dist/environment.js +59 -12
  9. package/dist/file-helpers.js +108 -2
  10. package/dist/frameworks.js +15 -1
  11. package/dist/index.js +12 -5
  12. package/dist/integrations/shadcn.js +10 -4
  13. package/dist/options.js +9 -0
  14. package/dist/package-json.js +7 -4
  15. package/dist/special-steps/index.js +24 -0
  16. package/dist/special-steps/rimraf-node-modules.js +16 -0
  17. package/dist/template-file.js +3 -13
  18. package/dist/types/add-ons.d.ts +3 -4
  19. package/dist/types/add-to-app.d.ts +16 -3
  20. package/dist/types/config-file.d.ts +4 -3
  21. package/dist/types/create-app.d.ts +1 -7
  22. package/dist/types/custom-add-ons/add-on.d.ts +69 -0
  23. package/dist/types/custom-add-ons/shared.d.ts +15 -0
  24. package/dist/types/custom-add-ons/starter.d.ts +7 -0
  25. package/dist/types/environment.d.ts +2 -1
  26. package/dist/types/file-helpers.d.ts +10 -0
  27. package/dist/types/frameworks.d.ts +2 -0
  28. package/dist/types/index.d.ts +13 -6
  29. package/dist/types/integrations/shadcn.d.ts +1 -1
  30. package/dist/types/options.d.ts +2 -0
  31. package/dist/types/package-json.d.ts +5 -0
  32. package/dist/types/package-manager.d.ts +6 -2
  33. package/dist/types/special-steps/index.d.ts +2 -0
  34. package/dist/types/special-steps/rimraf-node-modules.d.ts +2 -0
  35. package/dist/types/template-file.d.ts +1 -1
  36. package/dist/types/types.d.ts +752 -70
  37. package/dist/types.js +65 -1
  38. package/package.json +9 -3
  39. package/src/add-ons.ts +7 -19
  40. package/src/add-to-app.ts +196 -102
  41. package/src/config-file.ts +16 -13
  42. package/src/create-app.ts +129 -75
  43. package/src/custom-add-ons/add-on.ts +261 -0
  44. package/src/custom-add-ons/shared.ts +161 -0
  45. package/src/custom-add-ons/starter.ts +126 -0
  46. package/src/environment.ts +70 -11
  47. package/src/file-helpers.ts +164 -2
  48. package/src/frameworks.ts +21 -1
  49. package/src/index.ts +46 -11
  50. package/src/integrations/shadcn.ts +14 -4
  51. package/src/options.ts +11 -0
  52. package/src/package-json.ts +13 -6
  53. package/src/special-steps/index.ts +36 -0
  54. package/src/special-steps/rimraf-node-modules.ts +25 -0
  55. package/src/template-file.ts +3 -18
  56. package/src/types.ts +143 -85
  57. package/tests/add-ons.test.ts +5 -5
  58. package/tests/add-to-app.test.ts +358 -0
  59. package/tests/config-file.test.ts +15 -11
  60. package/tests/create-app.test.ts +43 -67
  61. package/tests/custom-add-ons/add-on.test.ts +12 -0
  62. package/tests/custom-add-ons/shared.test.ts +257 -0
  63. package/tests/custom-add-ons/starter.test.ts +58 -0
  64. package/tests/environment.test.ts +19 -0
  65. package/tests/integrations/shadcn.test.ts +48 -63
  66. package/tests/options.test.ts +42 -0
  67. package/tests/setupVitest.ts +6 -0
  68. package/tests/template-file.test.ts +54 -91
  69. package/vitest.config.ts +2 -0
package/src/create-app.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { basename, resolve } from 'node:path'
2
2
 
3
- import { getBinaryFile } from './file-helpers.js'
3
+ import { isBase64 } from './file-helpers.js'
4
4
  import { formatCommand } from './utils.js'
5
- import { writeConfigFile } from './config-file.js'
5
+ import { writeConfigFileToEnvironment } from './config-file.js'
6
6
  import {
7
7
  getPackageManagerScriptCommand,
8
8
  packageManagerInstall,
@@ -11,93 +11,164 @@ import { createPackageJSON } from './package-json.js'
11
11
  import { createTemplateFile } from './template-file.js'
12
12
  import { installShadcnComponents } from './integrations/shadcn.js'
13
13
  import { setupGit } from './integrations/git.js'
14
+ import { runSpecialSteps } from './special-steps/index.js'
14
15
 
15
16
  import type { Environment, FileBundleHandler, Options } from './types.js'
16
17
 
17
- async function writeFiles(
18
- environment: Environment,
19
- targetDir: string,
20
- options: Options,
21
- ) {
22
- const templateFileFromContent = createTemplateFile(
23
- environment,
24
- options,
25
- targetDir,
26
- )
18
+ async function writeFiles(environment: Environment, options: Options) {
19
+ const templateFileFromContent = createTemplateFile(environment, options)
27
20
 
28
21
  async function writeFileBundle(bundle: FileBundleHandler) {
29
22
  const files = await bundle.getFiles()
30
23
  for (const file of files) {
31
24
  const contents = await bundle.getFileContents(file)
32
- const binaryFile = getBinaryFile(contents)
33
- if (binaryFile) {
34
- await environment.writeFile(resolve(targetDir, file), binaryFile)
25
+ const isBinaryFile = isBase64(contents)
26
+ if (isBinaryFile) {
27
+ await environment.writeFileBase64(
28
+ resolve(options.targetDir, file),
29
+ contents,
30
+ )
35
31
  } else {
36
32
  await templateFileFromContent(file, contents)
37
33
  }
38
34
  }
35
+
36
+ const deletedFiles = await bundle.getDeletedFiles()
37
+ for (const file of deletedFiles) {
38
+ await environment.deleteFile(resolve(options.targetDir, file))
39
+ }
39
40
  }
40
41
 
42
+ environment.startStep({
43
+ id: 'write-framework-files',
44
+ type: 'file',
45
+ message: 'Writing framework files...',
46
+ })
41
47
  await writeFileBundle(options.framework)
48
+ environment.finishStep('write-framework-files', 'Framework files written')
42
49
 
50
+ let wroteAddonFiles = false
43
51
  for (const type of ['add-on', 'example', 'toolchain']) {
44
52
  for (const phase of ['setup', 'add-on', 'example']) {
45
53
  for (const addOn of options.chosenAddOns.filter(
46
54
  (addOn) => addOn.phase === phase && addOn.type === type,
47
55
  )) {
56
+ environment.startStep({
57
+ id: 'write-addon-files',
58
+ type: 'file',
59
+ message: `Writing ${addOn.name} files...`,
60
+ })
48
61
  await writeFileBundle(addOn)
62
+ wroteAddonFiles = true
49
63
  }
50
64
  }
51
65
  }
66
+ if (wroteAddonFiles) {
67
+ environment.finishStep('write-addon-files', 'Add-on files written')
68
+ }
52
69
 
53
70
  if (options.starter) {
71
+ environment.startStep({
72
+ id: 'write-starter-files',
73
+ type: 'file',
74
+ message: 'Writing starter files...',
75
+ })
54
76
  await writeFileBundle(options.starter)
77
+ environment.finishStep('write-starter-files', 'Starter files written')
55
78
  }
56
79
 
80
+ environment.startStep({
81
+ id: 'write-package-json',
82
+ type: 'file',
83
+ message: 'Writing package.json...',
84
+ })
57
85
  await environment.writeFile(
58
- resolve(targetDir, './package.json'),
86
+ resolve(options.targetDir, './package.json'),
59
87
  JSON.stringify(createPackageJSON(options), null, 2),
60
88
  )
61
-
62
- await writeConfigFile(environment, targetDir, options)
89
+ environment.finishStep('write-package-json', 'Package.json written')
90
+
91
+ environment.startStep({
92
+ id: 'write-config-file',
93
+ type: 'file',
94
+ message: 'Writing config file...',
95
+ })
96
+ await writeConfigFileToEnvironment(environment, options)
97
+ environment.finishStep('write-config-file', 'Config file written')
63
98
  }
64
99
 
65
100
  async function runCommandsAndInstallDependencies(
66
101
  environment: Environment,
67
- targetDir: string,
68
102
  options: Options,
69
- silent: boolean,
70
103
  ) {
71
- const s = silent ? null : environment.spinner()
104
+ const s = environment.spinner()
72
105
 
73
106
  // Setup git
74
107
  if (options.git) {
75
- s?.start(`Initializing git repository...`)
76
- await setupGit(environment, targetDir)
77
- s?.stop(`Initialized git repository`)
108
+ s.start(`Initializing git repository...`)
109
+ environment.startStep({
110
+ id: 'initialize-git-repository',
111
+ type: 'command',
112
+ message: 'Initializing git repository...',
113
+ })
114
+
115
+ await setupGit(environment, options.targetDir)
116
+
117
+ environment.finishStep(
118
+ 'initialize-git-repository',
119
+ 'Initialized git repository',
120
+ )
121
+ s.stop(`Initialized git repository`)
122
+ }
123
+
124
+ // Run any special steps for the new add-ons
125
+ const specialSteps = new Set<string>([])
126
+ for (const addOn of options.chosenAddOns) {
127
+ for (const step of addOn.createSpecialSteps || []) {
128
+ specialSteps.add(step)
129
+ }
130
+ }
131
+ if (specialSteps.size) {
132
+ await runSpecialSteps(environment, options, Array.from(specialSteps))
78
133
  }
79
134
 
80
135
  // Install dependencies
81
- s?.start(`Installing dependencies via ${options.packageManager}...`)
136
+ s.start(`Installing dependencies via ${options.packageManager}...`)
137
+ environment.startStep({
138
+ id: 'install-dependencies',
139
+ type: 'package-manager',
140
+ message: `Installing dependencies via ${options.packageManager}...`,
141
+ })
82
142
  await packageManagerInstall(
83
143
  environment,
84
- resolve(targetDir),
144
+ options.targetDir,
85
145
  options.packageManager,
86
146
  )
87
- s?.stop(`Installed dependencies`)
147
+ environment.finishStep('install-dependencies', 'Installed dependencies')
148
+ s.stop(`Installed dependencies`)
88
149
 
89
150
  for (const phase of ['setup', 'add-on', 'example']) {
90
151
  for (const addOn of options.chosenAddOns.filter(
91
152
  (addOn) =>
92
153
  addOn.phase === phase && addOn.command && addOn.command.command,
93
154
  )) {
94
- s?.start(`Setting up ${addOn.name}...`)
155
+ s.start(`Running commands for ${addOn.name}...`)
156
+ const cmd = formatCommand({
157
+ command: addOn.command!.command,
158
+ args: addOn.command!.args || [],
159
+ })
160
+ environment.startStep({
161
+ id: 'run-commands',
162
+ type: 'command',
163
+ message: cmd,
164
+ })
95
165
  await environment.execute(
96
166
  addOn.command!.command,
97
167
  addOn.command!.args || [],
98
- resolve(targetDir),
168
+ options.targetDir,
99
169
  )
100
- s?.stop(`${addOn.name} setup complete`)
170
+ environment.finishStep('run-commands', 'Setup commands complete')
171
+ s.stop(`${addOn.name} commands complete`)
101
172
  }
102
173
  }
103
174
 
@@ -107,24 +178,31 @@ async function runCommandsAndInstallDependencies(
107
178
  options.starter.command &&
108
179
  options.starter.command.command
109
180
  ) {
110
- s?.start(`Setting up starter ${options.starter.name}...`)
181
+ s.start(`Setting up starter ${options.starter.name}...`)
182
+ const cmd = formatCommand({
183
+ command: options.starter.command.command,
184
+ args: options.starter.command.args || [],
185
+ })
186
+ environment.startStep({
187
+ id: 'run-starter-command',
188
+ type: 'command',
189
+ message: cmd,
190
+ })
191
+
111
192
  await environment.execute(
112
193
  options.starter.command.command,
113
194
  options.starter.command.args || [],
114
- resolve(targetDir),
195
+ options.targetDir,
115
196
  )
116
- s?.stop(`Starter ${options.starter.name} setup complete`)
197
+
198
+ environment.finishStep('run-starter-command', 'Starter command complete')
199
+ s.stop(`${options.starter.name} commands complete`)
117
200
  }
118
201
 
119
- await installShadcnComponents(environment, targetDir, options, silent)
202
+ await installShadcnComponents(environment, options.targetDir, options)
120
203
  }
121
204
 
122
- function report(
123
- environment: Environment,
124
- options: Options,
125
- appName: string,
126
- targetDir: string,
127
- ) {
205
+ function report(environment: Environment, options: Options) {
128
206
  const warnings: Array<string> = []
129
207
  for (const addOn of options.chosenAddOns) {
130
208
  if (addOn.warning) {
@@ -141,53 +219,29 @@ function report(
141
219
  if (environment.getErrors().length) {
142
220
  errorStatement = `
143
221
 
144
- Errors were encountered during this process:
222
+ Errors were encountered during the creation of your app:
145
223
 
146
224
  ${environment.getErrors().join('\n')}`
147
225
  }
148
226
 
149
- environment.outro(`Your ${appName} app is ready in '${basename(targetDir)}'.
227
+ environment.outro(
228
+ `Your ${environment.appName} app is ready in '${basename(options.targetDir)}'.
150
229
 
151
230
  Use the following commands to start your app:
152
231
  % cd ${options.projectName}
153
232
  % ${formatCommand(
154
- getPackageManagerScriptCommand(options.packageManager, ['dev']),
155
- )}
233
+ getPackageManagerScriptCommand(options.packageManager, ['dev']),
234
+ )}
156
235
 
157
- Please check the README.md for more information on testing, styling, adding routes, etc.${errorStatement}`)
236
+ Please check the README.md for more information on testing, styling, adding routes, etc.${errorStatement}`,
237
+ )
158
238
  }
159
239
 
160
- export async function createApp(
161
- options: Options,
162
- {
163
- silent = false,
164
- environment,
165
- cwd,
166
- appName = 'TanStack',
167
- }: {
168
- silent?: boolean
169
- environment: Environment
170
- cwd?: string
171
- name?: string
172
- appName?: string
173
- },
174
- ) {
240
+ export async function createApp(environment: Environment, options: Options) {
175
241
  environment.startRun()
176
-
177
- const targetDir: string = cwd || resolve(process.cwd(), options.projectName)
178
-
179
- await writeFiles(environment, targetDir, options)
180
-
181
- await runCommandsAndInstallDependencies(
182
- environment,
183
- targetDir,
184
- options,
185
- silent,
186
- )
187
-
242
+ await writeFiles(environment, options)
243
+ await runCommandsAndInstallDependencies(environment, options)
188
244
  environment.finishRun()
189
245
 
190
- if (!silent) {
191
- report(environment, options, appName, targetDir)
192
- }
246
+ report(environment, options)
193
247
  }
@@ -0,0 +1,261 @@
1
+ import { readFile } from 'node:fs/promises'
2
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs'
3
+ import { basename, dirname, resolve } from 'node:path'
4
+
5
+ import { AddOnCompiledSchema } from '../types.js'
6
+ import { createIgnore, recursivelyGatherFiles } from '../file-helpers.js'
7
+ import {
8
+ compareFilesRecursively,
9
+ createAppOptionsFromPersisted,
10
+ createPackageAdditions,
11
+ readCurrentProjectOptions,
12
+ runCreateApp,
13
+ } from './shared.js'
14
+
15
+ import type { PersistedOptions } from '../config-file'
16
+ import type {
17
+ AddOn,
18
+ AddOnCompiled,
19
+ AddOnInfo,
20
+ Environment,
21
+ Options,
22
+ } from '../types'
23
+
24
+ const ADD_ON_DIR = '.add-on'
25
+
26
+ export const ADD_ON_IGNORE_FILES: Array<string> = [
27
+ 'main.jsx',
28
+ 'App.jsx',
29
+ 'main.tsx',
30
+ 'App.tsx',
31
+ 'routeTree.gen.ts',
32
+ ]
33
+
34
+ const INFO_FILE = '.add-on/info.json'
35
+ const COMPILED_FILE = 'add-on.json'
36
+
37
+ const ASSETS_DIR = 'assets'
38
+
39
+ export function camelCase(str: string) {
40
+ return str
41
+ .split(/(\.|-|\/)/)
42
+ .filter((part) => /^[a-zA-Z]+$/.test(part))
43
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
44
+ .join('')
45
+ }
46
+
47
+ export function templatize(routeCode: string, routeFile: string) {
48
+ let code = routeCode
49
+
50
+ // Replace the import
51
+ code = code.replace(
52
+ /import { createFileRoute } from ['"]@tanstack\/react-router['"]/g,
53
+ `import { <% if (fileRouter) { %>createFileRoute<% } else { %>createRoute<% } %> } from '@tanstack/react-router'`,
54
+ )
55
+
56
+ // Extract route path and definition, then transform the route declaration
57
+ const routeMatch = code.match(
58
+ /export\s+const\s+Route\s*=\s*createFileRoute\(['"]([^'"]+)['"]\)\s*\(\{([^}]+)\}\)/,
59
+ )
60
+
61
+ let path = ''
62
+
63
+ if (routeMatch) {
64
+ const fullMatch = routeMatch[0]
65
+ path = routeMatch[1]
66
+ const routeDefinition = routeMatch[2]
67
+ code = code.replace(
68
+ fullMatch,
69
+ `<% if (codeRouter) { %>
70
+ import type { RootRoute } from '@tanstack/react-router'
71
+ <% } else { %>
72
+ export const Route = createFileRoute('${path}')({${routeDefinition}})
73
+ <% } %>`,
74
+ )
75
+
76
+ code += `
77
+ <% if (codeRouter) { %>
78
+ export default (parentRoute: RootRoute) => createRoute({
79
+ path: '${path}',
80
+ ${routeDefinition}
81
+ getParentRoute: () => parentRoute,
82
+ })
83
+ <% } %>
84
+ `
85
+ } else {
86
+ console.error(`No route found in the file: ${routeFile}`)
87
+ }
88
+
89
+ const name = basename(path)
90
+ .replace('.tsx', '')
91
+ .replace(/^demo/, '')
92
+ .replace('.', ' ')
93
+ .trim()
94
+
95
+ const jsName = camelCase(basename(path))
96
+
97
+ return { url: path, code, name, jsName }
98
+ }
99
+
100
+ export async function validateAddOnSetup(environment: Environment) {
101
+ const options = await readCurrentProjectOptions(environment)
102
+
103
+ if (options.mode !== 'file-router') {
104
+ environment.error(
105
+ 'This project is not using file-router mode.',
106
+ 'To create an add-on, the project must be created with the file-router mode.',
107
+ )
108
+ process.exit(1)
109
+ }
110
+ if (!options.tailwind) {
111
+ environment.error(
112
+ 'This project is not using Tailwind CSS.',
113
+ 'To create an add-on, the project must be created with Tailwind CSS.',
114
+ )
115
+ process.exit(1)
116
+ }
117
+ if (!options.typescript) {
118
+ environment.error(
119
+ 'This project is not using TypeScript.',
120
+ 'To create an add-on, the project must be created with TypeScript.',
121
+ )
122
+ process.exit(1)
123
+ }
124
+ }
125
+
126
+ export async function readOrGenerateAddOnInfo(
127
+ options: PersistedOptions,
128
+ ): Promise<AddOnInfo> {
129
+ return existsSync(INFO_FILE)
130
+ ? JSON.parse((await readFile(INFO_FILE)).toString())
131
+ : ({
132
+ id: `${options.projectName}-add-on`,
133
+ name: `${options.projectName}-add-on`,
134
+ version: '0.0.1',
135
+ description: 'Add-on',
136
+ author: 'Jane Smith <jane.smith@example.com>',
137
+ license: 'MIT',
138
+ link: `https://github.com/jane-smith/${options.projectName}-add-on`,
139
+ shadcnComponents: [],
140
+ framework: options.framework,
141
+ modes: [options.mode],
142
+ routes: [],
143
+ warning: '',
144
+ phase: 'add-on',
145
+ type: 'add-on',
146
+ packageAdditions: {
147
+ scripts: {},
148
+ dependencies: {},
149
+ devDependencies: {},
150
+ },
151
+ dependsOn: options.existingAddOns,
152
+ } as AddOnInfo)
153
+ }
154
+
155
+ export async function generateProject(persistedOptions: PersistedOptions) {
156
+ const info = await readOrGenerateAddOnInfo(persistedOptions)
157
+
158
+ const output = await runCreateApp(
159
+ (await createAppOptionsFromPersisted(
160
+ persistedOptions,
161
+ )) as Required<Options>,
162
+ )
163
+
164
+ return { info, output }
165
+ }
166
+
167
+ export async function buildAssetsDirectory(
168
+ output: {
169
+ files: Record<string, string>
170
+ },
171
+ info: AddOnInfo,
172
+ ) {
173
+ const assetsDir = resolve(ADD_ON_DIR, ASSETS_DIR)
174
+ const ignore = createIgnore(process.cwd())
175
+
176
+ if (!existsSync(assetsDir)) {
177
+ const changedFiles: Record<string, string> = {}
178
+ await compareFilesRecursively('.', ignore, output.files, changedFiles)
179
+
180
+ for (const file of Object.keys(changedFiles).filter(
181
+ (file) => !ADD_ON_IGNORE_FILES.includes(basename(file)),
182
+ )) {
183
+ mkdirSync(dirname(resolve(assetsDir, file)), {
184
+ recursive: true,
185
+ })
186
+ if (file.includes('/routes/')) {
187
+ const { url, code, name, jsName } = templatize(changedFiles[file], file)
188
+ info.routes ||= []
189
+ if (!info.routes.find((r) => r.url === url)) {
190
+ info.routes.push({
191
+ url,
192
+ name,
193
+ jsName,
194
+ path: file,
195
+ })
196
+ }
197
+ writeFileSync(resolve(assetsDir, `${file}.ejs`), code)
198
+ } else {
199
+ writeFileSync(resolve(assetsDir, file), changedFiles[file])
200
+ }
201
+ }
202
+ }
203
+ }
204
+
205
+ export async function updateAddOnInfo(environment: Environment) {
206
+ const { info, output } = await generateProject(
207
+ await readCurrentProjectOptions(environment),
208
+ )
209
+
210
+ info.packageAdditions = createPackageAdditions(
211
+ JSON.parse(output.files['./package.json']),
212
+ JSON.parse((await readFile('package.json')).toString()),
213
+ )
214
+
215
+ await buildAssetsDirectory(output, info)
216
+
217
+ mkdirSync(resolve(dirname(INFO_FILE)), { recursive: true })
218
+ writeFileSync(INFO_FILE, JSON.stringify(info, null, 2))
219
+ }
220
+
221
+ export async function compileAddOn(environment: Environment) {
222
+ const info = await readOrGenerateAddOnInfo(
223
+ await readCurrentProjectOptions(environment),
224
+ )
225
+
226
+ const assetsDir = resolve(ADD_ON_DIR, ASSETS_DIR)
227
+
228
+ const compiledInfo: AddOnCompiled = {
229
+ ...info,
230
+ files: await recursivelyGatherFiles(assetsDir),
231
+ deletedFiles: [],
232
+ }
233
+
234
+ writeFileSync(COMPILED_FILE, JSON.stringify(compiledInfo, null, 2))
235
+ }
236
+
237
+ export async function initAddOn(environment: Environment) {
238
+ await validateAddOnSetup(environment)
239
+ await updateAddOnInfo(environment)
240
+ await compileAddOn(environment)
241
+ }
242
+
243
+ export async function loadRemoteAddOn(url: string): Promise<AddOn> {
244
+ const response = await fetch(url)
245
+ const jsonContent = await response.json()
246
+
247
+ const checked = AddOnCompiledSchema.safeParse(jsonContent)
248
+ if (!checked.success) {
249
+ throw new Error(`Invalid add-on: ${url}`)
250
+ }
251
+
252
+ const addOn = checked.data
253
+ addOn.id = url
254
+ const out = {
255
+ ...addOn,
256
+ getFiles: () => Promise.resolve(Object.keys(addOn.files)),
257
+ getFileContents: (path: string) => Promise.resolve(addOn.files[path]),
258
+ getDeletedFiles: () => Promise.resolve(addOn.deletedFiles),
259
+ }
260
+ return out
261
+ }