@tanstack/cta-engine 0.10.0-alpha.26 → 0.10.0-alpha.30
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/dist/add-to-app.js +5 -3
- package/dist/config-file.js +21 -3
- package/dist/custom-add-ons/add-on.js +1 -1
- package/dist/custom-add-ons/shared.js +1 -2
- package/dist/custom-add-ons/starter.js +1 -1
- package/dist/environment.js +3 -12
- package/dist/index.js +2 -1
- package/dist/registry.js +56 -0
- package/dist/types/config-file.d.ts +2 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/registry.d.ts +25 -0
- package/package.json +1 -1
- package/src/add-to-app.ts +5 -3
- package/src/config-file.ts +26 -4
- package/src/custom-add-ons/add-on.ts +1 -1
- package/src/custom-add-ons/shared.ts +1 -2
- package/src/custom-add-ons/starter.ts +1 -1
- package/src/environment.ts +3 -12
- package/src/index.ts +8 -0
- package/src/registry.ts +92 -0
- package/tests/add-to-app.test.ts +2 -2
- package/tests/config-file.test.ts +39 -2
- package/tests/custom-add-ons/shared.test.ts +0 -4
- package/tests/custom-add-ons/starter.test.ts +3 -3
package/dist/add-to-app.js
CHANGED
|
@@ -10,23 +10,25 @@ import { packageManagerInstall } from './package-manager.js';
|
|
|
10
10
|
import { isBase64, recursivelyGatherFilesFromEnvironment, } from './file-helpers.js';
|
|
11
11
|
import { mergePackageJSON } from './package-json.js';
|
|
12
12
|
import { runSpecialSteps } from './special-steps/index.js';
|
|
13
|
+
import { loadStarter } from './custom-add-ons/starter.js';
|
|
13
14
|
export async function hasPendingGitChanges(environment, cwd) {
|
|
14
15
|
const { stdout } = await environment.execute('git', ['status', '--porcelain'], cwd);
|
|
15
16
|
return stdout.length > 0;
|
|
16
17
|
}
|
|
17
18
|
async function createOptions(json, addOns, targetDir) {
|
|
18
19
|
const framework = getFrameworkById(json.framework);
|
|
19
|
-
|
|
20
|
+
const starter = json.starter ? await loadStarter(json.starter) : undefined;
|
|
20
21
|
return {
|
|
21
22
|
...json,
|
|
22
23
|
framework,
|
|
23
24
|
tailwind: true,
|
|
24
25
|
addOns: true,
|
|
25
26
|
chosenAddOns: await finalizeAddOns(framework, json.mode, [
|
|
26
|
-
...json.
|
|
27
|
+
...json.chosenAddOns,
|
|
27
28
|
...addOns,
|
|
28
29
|
]),
|
|
29
30
|
targetDir,
|
|
31
|
+
starter,
|
|
30
32
|
};
|
|
31
33
|
}
|
|
32
34
|
async function runCreateApp(options) {
|
|
@@ -165,5 +167,5 @@ export async function addToApp(environment, addOns, cwd, options) {
|
|
|
165
167
|
});
|
|
166
168
|
writeConfigFileToEnvironment(environment, newOptions);
|
|
167
169
|
environment.finishStep('write-config-file', 'Config file written');
|
|
168
|
-
environment.outro(
|
|
170
|
+
environment.outro(`Add-ons ${addOns.join(', ')} added!`);
|
|
169
171
|
}
|
package/dist/config-file.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
2
|
import { CONFIG_FILE } from './constants.js';
|
|
3
|
+
import { createDefaultEnvironment } from './environment.js';
|
|
3
4
|
function createPersistedOptions(options) {
|
|
4
5
|
/* eslint-disable unused-imports/no-unused-vars */
|
|
5
6
|
const { chosenAddOns, framework, targetDir, ...rest } = options;
|
|
@@ -8,7 +9,7 @@ function createPersistedOptions(options) {
|
|
|
8
9
|
...rest,
|
|
9
10
|
version: 1,
|
|
10
11
|
framework: options.framework.id,
|
|
11
|
-
|
|
12
|
+
chosenAddOns: options.chosenAddOns.map((addOn) => addOn.id),
|
|
12
13
|
starter: options.starter?.id ?? undefined,
|
|
13
14
|
};
|
|
14
15
|
}
|
|
@@ -19,10 +20,27 @@ export async function readConfigFileFromEnvironment(environment, targetDir) {
|
|
|
19
20
|
try {
|
|
20
21
|
const configFile = resolve(targetDir, CONFIG_FILE);
|
|
21
22
|
const config = await environment.readFile(configFile);
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
const originalJSON = JSON.parse(config);
|
|
24
|
+
// Look for markers out outdated config files and upgrade the format on the fly (it will be written in the updated version after we add add-ons)
|
|
25
|
+
if (originalJSON.existingAddOns) {
|
|
26
|
+
if (originalJSON.framework === 'react') {
|
|
27
|
+
originalJSON.framework = 'react-cra';
|
|
28
|
+
}
|
|
29
|
+
originalJSON.chosenAddOns = originalJSON.existingAddOns;
|
|
30
|
+
delete originalJSON.existingAddOns;
|
|
31
|
+
delete originalJSON.addOns;
|
|
32
|
+
if (originalJSON.toolchain && originalJSON.toolchain !== 'none') {
|
|
33
|
+
originalJSON.chosenAddOns.push(originalJSON.toolchain);
|
|
34
|
+
}
|
|
35
|
+
delete originalJSON.toolchain;
|
|
36
|
+
delete originalJSON.variableValues;
|
|
37
|
+
}
|
|
38
|
+
return originalJSON;
|
|
24
39
|
}
|
|
25
40
|
catch {
|
|
26
41
|
return null;
|
|
27
42
|
}
|
|
28
43
|
}
|
|
44
|
+
export async function readConfigFile(targetDir) {
|
|
45
|
+
return readConfigFileFromEnvironment(createDefaultEnvironment(), targetDir);
|
|
46
|
+
}
|
|
@@ -97,7 +97,7 @@ export async function readOrGenerateAddOnInfo(options) {
|
|
|
97
97
|
dependencies: {},
|
|
98
98
|
devDependencies: {},
|
|
99
99
|
},
|
|
100
|
-
dependsOn: options.
|
|
100
|
+
dependsOn: options.chosenAddOns,
|
|
101
101
|
};
|
|
102
102
|
}
|
|
103
103
|
export async function generateProject(persistedOptions) {
|
|
@@ -56,7 +56,7 @@ export async function createAppOptionsFromPersisted(json) {
|
|
|
56
56
|
framework: framework,
|
|
57
57
|
starter: json.starter ? await loadStarter(json.starter) : undefined,
|
|
58
58
|
chosenAddOns: await finalizeAddOns(framework, json.mode, [
|
|
59
|
-
...json.
|
|
59
|
+
...json.chosenAddOns,
|
|
60
60
|
]),
|
|
61
61
|
};
|
|
62
62
|
}
|
|
@@ -75,7 +75,6 @@ export function createSerializedOptionsFromPersisted(json) {
|
|
|
75
75
|
targetDir: '',
|
|
76
76
|
framework: json.framework,
|
|
77
77
|
starter: json.starter,
|
|
78
|
-
chosenAddOns: json.existingAddOns,
|
|
79
78
|
};
|
|
80
79
|
}
|
|
81
80
|
export async function runCreateApp(options) {
|
package/dist/environment.js
CHANGED
|
@@ -78,23 +78,14 @@ export function createMemoryEnvironment(returnPathsRelativeTo = '') {
|
|
|
78
78
|
deletedFiles: [],
|
|
79
79
|
};
|
|
80
80
|
const { fs, vol } = memfs({});
|
|
81
|
-
const cwd = process.cwd();
|
|
82
|
-
const isInCwd = (path) => path.startsWith(cwd);
|
|
83
|
-
const isTemplatePath = (path) => !isInCwd(path);
|
|
84
81
|
environment.appendFile = async (path, contents) => {
|
|
85
82
|
fs.mkdirSync(dirname(path), { recursive: true });
|
|
86
83
|
await fs.appendFileSync(path, contents);
|
|
87
84
|
};
|
|
88
85
|
environment.copyFile = async (from, to) => {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
fs.writeFileSync(to, contents);
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
fs.mkdirSync(dirname(to), { recursive: true });
|
|
96
|
-
fs.copyFileSync(from, to);
|
|
97
|
-
}
|
|
86
|
+
fs.mkdirSync(dirname(to), { recursive: true });
|
|
87
|
+
fs.copyFileSync(from, to);
|
|
88
|
+
return Promise.resolve();
|
|
98
89
|
};
|
|
99
90
|
environment.execute = async (command, args) => {
|
|
100
91
|
output.commands.push({
|
package/dist/index.js
CHANGED
|
@@ -7,11 +7,12 @@ export { createMemoryEnvironment, createDefaultEnvironment, } from './environmen
|
|
|
7
7
|
export { CODE_ROUTER, CONFIG_FILE, FILE_ROUTER } from './constants.js';
|
|
8
8
|
export { DEFAULT_PACKAGE_MANAGER, SUPPORTED_PACKAGE_MANAGERS, getPackageManager, } from './package-manager.js';
|
|
9
9
|
export { registerFramework, getFrameworkById, getFrameworkByName, getFrameworks, } from './frameworks.js';
|
|
10
|
-
export { writeConfigFileToEnvironment, readConfigFileFromEnvironment, } from './config-file.js';
|
|
10
|
+
export { writeConfigFileToEnvironment, readConfigFileFromEnvironment, readConfigFile, } from './config-file.js';
|
|
11
11
|
export { cleanUpFiles, cleanUpFileArray, readFileHelper, getBinaryFile, recursivelyGatherFiles, relativePath, } from './file-helpers.js';
|
|
12
12
|
export { formatCommand } from './utils.js';
|
|
13
13
|
export { initStarter, compileStarter } from './custom-add-ons/starter.js';
|
|
14
14
|
export { initAddOn, compileAddOn } from './custom-add-ons/add-on.js';
|
|
15
15
|
export { createAppOptionsFromPersisted, createSerializedOptionsFromPersisted, } from './custom-add-ons/shared.js';
|
|
16
16
|
export { createSerializedOptions } from './options.js';
|
|
17
|
+
export { getRawRegistry, getRegistry, getRegistryAddOns, getRegistryStarters, } from './registry.js';
|
|
17
18
|
export { StarterCompiledSchema, AddOnCompiledSchema, AddOnInfoSchema, IntegrationSchema, } from './types.js';
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { loadRemoteAddOn } from './custom-add-ons/add-on.js';
|
|
2
|
+
import { loadStarter } from './custom-add-ons/starter.js';
|
|
3
|
+
function absolutizeUrl(originalUrl, relativeUrl) {
|
|
4
|
+
if (relativeUrl.startsWith('http') || relativeUrl.startsWith('https')) {
|
|
5
|
+
return relativeUrl;
|
|
6
|
+
}
|
|
7
|
+
const baseUrl = originalUrl.replace(/registry.json$/, '');
|
|
8
|
+
return `${baseUrl}${relativeUrl.replace(/^\.\//, '')}`;
|
|
9
|
+
}
|
|
10
|
+
export async function getRawRegistry(registryUrl) {
|
|
11
|
+
const regUrl = registryUrl || process.env.CTA_REGISTRY;
|
|
12
|
+
if (regUrl) {
|
|
13
|
+
const registry = (await fetch(regUrl).then((res) => res.json()));
|
|
14
|
+
for (const addOn of registry['add-ons']) {
|
|
15
|
+
addOn.url = absolutizeUrl(regUrl, addOn.url);
|
|
16
|
+
}
|
|
17
|
+
for (const starter of registry.starters) {
|
|
18
|
+
starter.url = absolutizeUrl(regUrl, starter.url);
|
|
19
|
+
if (starter.banner) {
|
|
20
|
+
starter.banner = absolutizeUrl(regUrl, starter.banner);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return registry;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function getAddOns(registry) {
|
|
27
|
+
const addOns = [];
|
|
28
|
+
for (const addOnInfo of registry['add-ons']) {
|
|
29
|
+
const addOn = await loadRemoteAddOn(addOnInfo.url);
|
|
30
|
+
addOns.push(addOn);
|
|
31
|
+
}
|
|
32
|
+
return addOns;
|
|
33
|
+
}
|
|
34
|
+
export async function getRegistryAddOns(registryUrl) {
|
|
35
|
+
const registry = await getRawRegistry(registryUrl);
|
|
36
|
+
return registry ? await getAddOns(registry) : [];
|
|
37
|
+
}
|
|
38
|
+
async function getStarters(registry) {
|
|
39
|
+
const starters = [];
|
|
40
|
+
for (const starterInfo of registry.starters) {
|
|
41
|
+
const starter = await loadStarter(starterInfo.url);
|
|
42
|
+
starters.push(starter);
|
|
43
|
+
}
|
|
44
|
+
return starters;
|
|
45
|
+
}
|
|
46
|
+
export async function getRegistryStarters(registryUrl) {
|
|
47
|
+
const registry = await getRawRegistry(registryUrl);
|
|
48
|
+
return registry ? await getStarters(registry) : [];
|
|
49
|
+
}
|
|
50
|
+
export async function getRegistry(registryUrl) {
|
|
51
|
+
const registry = await getRawRegistry(registryUrl);
|
|
52
|
+
return {
|
|
53
|
+
addOns: registry ? await getAddOns(registry) : [],
|
|
54
|
+
starters: registry ? await getStarters(registry) : [],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -2,8 +2,9 @@ import type { Environment, Options } from './types.js';
|
|
|
2
2
|
export type PersistedOptions = Omit<Partial<Options>, 'addOns' | 'chosenAddOns' | 'framework' | 'starter' | 'targetDir'> & {
|
|
3
3
|
framework: string;
|
|
4
4
|
version: number;
|
|
5
|
-
|
|
5
|
+
chosenAddOns: Array<string>;
|
|
6
6
|
starter?: string;
|
|
7
7
|
};
|
|
8
8
|
export declare function writeConfigFileToEnvironment(environment: Environment, options: Options): Promise<void>;
|
|
9
9
|
export declare function readConfigFileFromEnvironment(environment: Environment, targetDir: string): Promise<PersistedOptions | null>;
|
|
10
|
+
export declare function readConfigFile(targetDir: string): Promise<PersistedOptions | null>;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -7,13 +7,14 @@ export { createMemoryEnvironment, createDefaultEnvironment, } from './environmen
|
|
|
7
7
|
export { CODE_ROUTER, CONFIG_FILE, FILE_ROUTER } from './constants.js';
|
|
8
8
|
export { DEFAULT_PACKAGE_MANAGER, SUPPORTED_PACKAGE_MANAGERS, getPackageManager, } from './package-manager.js';
|
|
9
9
|
export { registerFramework, getFrameworkById, getFrameworkByName, getFrameworks, } from './frameworks.js';
|
|
10
|
-
export { writeConfigFileToEnvironment, readConfigFileFromEnvironment, } from './config-file.js';
|
|
10
|
+
export { writeConfigFileToEnvironment, readConfigFileFromEnvironment, readConfigFile, } from './config-file.js';
|
|
11
11
|
export { cleanUpFiles, cleanUpFileArray, readFileHelper, getBinaryFile, recursivelyGatherFiles, relativePath, } from './file-helpers.js';
|
|
12
12
|
export { formatCommand } from './utils.js';
|
|
13
13
|
export { initStarter, compileStarter } from './custom-add-ons/starter.js';
|
|
14
14
|
export { initAddOn, compileAddOn } from './custom-add-ons/add-on.js';
|
|
15
15
|
export { createAppOptionsFromPersisted, createSerializedOptionsFromPersisted, } from './custom-add-ons/shared.js';
|
|
16
16
|
export { createSerializedOptions } from './options.js';
|
|
17
|
+
export { getRawRegistry, getRegistry, getRegistryAddOns, getRegistryStarters, } from './registry.js';
|
|
17
18
|
export { StarterCompiledSchema, StatusEvent, StatusStepType, StopEvent, AddOnCompiledSchema, AddOnInfoSchema, IntegrationSchema, } from './types.js';
|
|
18
19
|
export type { AddOn, Environment, FileBundleHandler, Framework, FrameworkDefinition, Mode, Options, SerializedOptions, Starter, StarterCompiled, } from './types.js';
|
|
19
20
|
export type { PersistedOptions } from './config-file.js';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AddOn, Mode, Starter } from './types';
|
|
2
|
+
export type Registry = {
|
|
3
|
+
starters: Array<{
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
url: string;
|
|
7
|
+
banner?: string;
|
|
8
|
+
mode: Mode;
|
|
9
|
+
framework: string;
|
|
10
|
+
}>;
|
|
11
|
+
'add-ons': Array<{
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
url: string;
|
|
15
|
+
modes: Array<Mode>;
|
|
16
|
+
framework: string;
|
|
17
|
+
}>;
|
|
18
|
+
};
|
|
19
|
+
export declare function getRawRegistry(registryUrl?: string): Promise<Registry | undefined>;
|
|
20
|
+
export declare function getRegistryAddOns(registryUrl?: string): Promise<Array<AddOn>>;
|
|
21
|
+
export declare function getRegistryStarters(registryUrl?: string): Promise<Array<Starter>>;
|
|
22
|
+
export declare function getRegistry(registryUrl?: string): Promise<{
|
|
23
|
+
addOns: Array<AddOn>;
|
|
24
|
+
starters: Array<Starter>;
|
|
25
|
+
}>;
|
package/package.json
CHANGED
package/src/add-to-app.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
} from './file-helpers.js'
|
|
18
18
|
import { mergePackageJSON } from './package-json.js'
|
|
19
19
|
import { runSpecialSteps } from './special-steps/index.js'
|
|
20
|
+
import { loadStarter } from './custom-add-ons/starter.js'
|
|
20
21
|
|
|
21
22
|
import type { Environment, Mode, Options } from './types.js'
|
|
22
23
|
import type { PersistedOptions } from './config-file.js'
|
|
@@ -40,7 +41,7 @@ async function createOptions(
|
|
|
40
41
|
): Promise<Options> {
|
|
41
42
|
const framework = getFrameworkById(json.framework)
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
const starter = json.starter ? await loadStarter(json.starter) : undefined
|
|
44
45
|
|
|
45
46
|
return {
|
|
46
47
|
...json,
|
|
@@ -48,10 +49,11 @@ async function createOptions(
|
|
|
48
49
|
tailwind: true,
|
|
49
50
|
addOns: true,
|
|
50
51
|
chosenAddOns: await finalizeAddOns(framework!, json.mode as Mode, [
|
|
51
|
-
...json.
|
|
52
|
+
...json.chosenAddOns,
|
|
52
53
|
...addOns,
|
|
53
54
|
]),
|
|
54
55
|
targetDir,
|
|
56
|
+
starter,
|
|
55
57
|
} as Options
|
|
56
58
|
}
|
|
57
59
|
|
|
@@ -270,5 +272,5 @@ export async function addToApp(
|
|
|
270
272
|
writeConfigFileToEnvironment(environment, newOptions)
|
|
271
273
|
environment.finishStep('write-config-file', 'Config file written')
|
|
272
274
|
|
|
273
|
-
environment.outro(
|
|
275
|
+
environment.outro(`Add-ons ${addOns.join(', ')} added!`)
|
|
274
276
|
}
|
package/src/config-file.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolve } from 'node:path'
|
|
2
2
|
|
|
3
3
|
import { CONFIG_FILE } from './constants.js'
|
|
4
|
+
import { createDefaultEnvironment } from './environment.js'
|
|
4
5
|
|
|
5
6
|
import type { Environment, Options } from './types.js'
|
|
6
7
|
|
|
@@ -10,7 +11,7 @@ export type PersistedOptions = Omit<
|
|
|
10
11
|
> & {
|
|
11
12
|
framework: string
|
|
12
13
|
version: number
|
|
13
|
-
|
|
14
|
+
chosenAddOns: Array<string>
|
|
14
15
|
starter?: string
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -22,7 +23,7 @@ function createPersistedOptions(options: Options): PersistedOptions {
|
|
|
22
23
|
...rest,
|
|
23
24
|
version: 1,
|
|
24
25
|
framework: options.framework.id,
|
|
25
|
-
|
|
26
|
+
chosenAddOns: options.chosenAddOns.map((addOn) => addOn.id),
|
|
26
27
|
starter: options.starter?.id ?? undefined,
|
|
27
28
|
}
|
|
28
29
|
}
|
|
@@ -45,10 +46,31 @@ export async function readConfigFileFromEnvironment(
|
|
|
45
46
|
const configFile = resolve(targetDir, CONFIG_FILE)
|
|
46
47
|
const config = await environment.readFile(configFile)
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
const originalJSON = JSON.parse(config)
|
|
49
50
|
|
|
50
|
-
|
|
51
|
+
// Look for markers out outdated config files and upgrade the format on the fly (it will be written in the updated version after we add add-ons)
|
|
52
|
+
if (originalJSON.existingAddOns) {
|
|
53
|
+
if (originalJSON.framework === 'react') {
|
|
54
|
+
originalJSON.framework = 'react-cra'
|
|
55
|
+
}
|
|
56
|
+
originalJSON.chosenAddOns = originalJSON.existingAddOns
|
|
57
|
+
delete originalJSON.existingAddOns
|
|
58
|
+
delete originalJSON.addOns
|
|
59
|
+
if (originalJSON.toolchain && originalJSON.toolchain !== 'none') {
|
|
60
|
+
originalJSON.chosenAddOns.push(originalJSON.toolchain)
|
|
61
|
+
}
|
|
62
|
+
delete originalJSON.toolchain
|
|
63
|
+
delete originalJSON.variableValues
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return originalJSON
|
|
51
67
|
} catch {
|
|
52
68
|
return null
|
|
53
69
|
}
|
|
54
70
|
}
|
|
71
|
+
|
|
72
|
+
export async function readConfigFile(
|
|
73
|
+
targetDir: string,
|
|
74
|
+
): Promise<PersistedOptions | null> {
|
|
75
|
+
return readConfigFileFromEnvironment(createDefaultEnvironment(), targetDir)
|
|
76
|
+
}
|
|
@@ -78,7 +78,7 @@ export async function createAppOptionsFromPersisted(
|
|
|
78
78
|
framework: framework!,
|
|
79
79
|
starter: json.starter ? await loadStarter(json.starter) : undefined,
|
|
80
80
|
chosenAddOns: await finalizeAddOns(framework!, json.mode as Mode, [
|
|
81
|
-
...json.
|
|
81
|
+
...json.chosenAddOns,
|
|
82
82
|
]),
|
|
83
83
|
}
|
|
84
84
|
}
|
|
@@ -100,7 +100,6 @@ export function createSerializedOptionsFromPersisted(
|
|
|
100
100
|
targetDir: '',
|
|
101
101
|
framework: json.framework,
|
|
102
102
|
starter: json.starter,
|
|
103
|
-
chosenAddOns: json.existingAddOns,
|
|
104
103
|
}
|
|
105
104
|
}
|
|
106
105
|
|
package/src/environment.ts
CHANGED
|
@@ -111,23 +111,14 @@ export function createMemoryEnvironment(returnPathsRelativeTo: string = '') {
|
|
|
111
111
|
|
|
112
112
|
const { fs, vol } = memfs({})
|
|
113
113
|
|
|
114
|
-
const cwd = process.cwd()
|
|
115
|
-
const isInCwd = (path: string) => path.startsWith(cwd)
|
|
116
|
-
const isTemplatePath = (path: string) => !isInCwd(path)
|
|
117
|
-
|
|
118
114
|
environment.appendFile = async (path: string, contents: string) => {
|
|
119
115
|
fs.mkdirSync(dirname(path), { recursive: true })
|
|
120
116
|
await fs.appendFileSync(path, contents)
|
|
121
117
|
}
|
|
122
118
|
environment.copyFile = async (from: string, to: string) => {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
fs.writeFileSync(to, contents)
|
|
127
|
-
} else {
|
|
128
|
-
fs.mkdirSync(dirname(to), { recursive: true })
|
|
129
|
-
fs.copyFileSync(from, to)
|
|
130
|
-
}
|
|
119
|
+
fs.mkdirSync(dirname(to), { recursive: true })
|
|
120
|
+
fs.copyFileSync(from, to)
|
|
121
|
+
return Promise.resolve()
|
|
131
122
|
}
|
|
132
123
|
environment.execute = async (command: string, args: Array<string>) => {
|
|
133
124
|
output.commands.push({
|
package/src/index.ts
CHANGED
|
@@ -29,6 +29,7 @@ export {
|
|
|
29
29
|
export {
|
|
30
30
|
writeConfigFileToEnvironment,
|
|
31
31
|
readConfigFileFromEnvironment,
|
|
32
|
+
readConfigFile,
|
|
32
33
|
} from './config-file.js'
|
|
33
34
|
|
|
34
35
|
export {
|
|
@@ -51,6 +52,13 @@ export {
|
|
|
51
52
|
|
|
52
53
|
export { createSerializedOptions } from './options.js'
|
|
53
54
|
|
|
55
|
+
export {
|
|
56
|
+
getRawRegistry,
|
|
57
|
+
getRegistry,
|
|
58
|
+
getRegistryAddOns,
|
|
59
|
+
getRegistryStarters,
|
|
60
|
+
} from './registry.js'
|
|
61
|
+
|
|
54
62
|
export {
|
|
55
63
|
StarterCompiledSchema,
|
|
56
64
|
StatusEvent,
|
package/src/registry.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { loadRemoteAddOn } from './custom-add-ons/add-on.js'
|
|
2
|
+
import { loadStarter } from './custom-add-ons/starter.js'
|
|
3
|
+
|
|
4
|
+
import type { AddOn, Mode, Starter } from './types'
|
|
5
|
+
|
|
6
|
+
export type Registry = {
|
|
7
|
+
starters: Array<{
|
|
8
|
+
name: string
|
|
9
|
+
description: string
|
|
10
|
+
url: string
|
|
11
|
+
banner?: string
|
|
12
|
+
mode: Mode
|
|
13
|
+
framework: string
|
|
14
|
+
}>
|
|
15
|
+
'add-ons': Array<{
|
|
16
|
+
name: string
|
|
17
|
+
description: string
|
|
18
|
+
url: string
|
|
19
|
+
modes: Array<Mode>
|
|
20
|
+
framework: string
|
|
21
|
+
}>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function absolutizeUrl(originalUrl: string, relativeUrl: string) {
|
|
25
|
+
if (relativeUrl.startsWith('http') || relativeUrl.startsWith('https')) {
|
|
26
|
+
return relativeUrl
|
|
27
|
+
}
|
|
28
|
+
const baseUrl = originalUrl.replace(/registry.json$/, '')
|
|
29
|
+
return `${baseUrl}${relativeUrl.replace(/^\.\//, '')}`
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function getRawRegistry(
|
|
33
|
+
registryUrl?: string,
|
|
34
|
+
): Promise<Registry | undefined> {
|
|
35
|
+
const regUrl = registryUrl || process.env.CTA_REGISTRY
|
|
36
|
+
if (regUrl) {
|
|
37
|
+
const registry = (await fetch(regUrl).then((res) => res.json())) as Registry
|
|
38
|
+
for (const addOn of registry['add-ons']) {
|
|
39
|
+
addOn.url = absolutizeUrl(regUrl, addOn.url)
|
|
40
|
+
}
|
|
41
|
+
for (const starter of registry.starters) {
|
|
42
|
+
starter.url = absolutizeUrl(regUrl, starter.url)
|
|
43
|
+
if (starter.banner) {
|
|
44
|
+
starter.banner = absolutizeUrl(regUrl, starter.banner)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return registry
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function getAddOns(registry: Registry): Promise<Array<AddOn>> {
|
|
52
|
+
const addOns: Array<AddOn> = []
|
|
53
|
+
for (const addOnInfo of registry['add-ons']) {
|
|
54
|
+
const addOn = await loadRemoteAddOn(addOnInfo.url)
|
|
55
|
+
addOns.push(addOn)
|
|
56
|
+
}
|
|
57
|
+
return addOns
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function getRegistryAddOns(
|
|
61
|
+
registryUrl?: string,
|
|
62
|
+
): Promise<Array<AddOn>> {
|
|
63
|
+
const registry = await getRawRegistry(registryUrl)
|
|
64
|
+
return registry ? await getAddOns(registry) : []
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function getStarters(registry: Registry): Promise<Array<Starter>> {
|
|
68
|
+
const starters: Array<Starter> = []
|
|
69
|
+
for (const starterInfo of registry.starters) {
|
|
70
|
+
const starter = await loadStarter(starterInfo.url)
|
|
71
|
+
starters.push(starter)
|
|
72
|
+
}
|
|
73
|
+
return starters
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function getRegistryStarters(
|
|
77
|
+
registryUrl?: string,
|
|
78
|
+
): Promise<Array<Starter>> {
|
|
79
|
+
const registry = await getRawRegistry(registryUrl)
|
|
80
|
+
return registry ? await getStarters(registry) : []
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function getRegistry(registryUrl?: string): Promise<{
|
|
84
|
+
addOns: Array<AddOn>
|
|
85
|
+
starters: Array<Starter>
|
|
86
|
+
}> {
|
|
87
|
+
const registry = await getRawRegistry(registryUrl)
|
|
88
|
+
return {
|
|
89
|
+
addOns: registry ? await getAddOns(registry) : [],
|
|
90
|
+
starters: registry ? await getStarters(registry) : [],
|
|
91
|
+
}
|
|
92
|
+
}
|
package/tests/add-to-app.test.ts
CHANGED
|
@@ -24,7 +24,7 @@ const fakeCTAJSON: PersistedOptions = {
|
|
|
24
24
|
targetDir: '/foo',
|
|
25
25
|
framework: 'test',
|
|
26
26
|
mode: 'code-router',
|
|
27
|
-
|
|
27
|
+
chosenAddOns: [],
|
|
28
28
|
version: 1,
|
|
29
29
|
typescript: true,
|
|
30
30
|
tailwind: true,
|
|
@@ -88,7 +88,7 @@ const configFile: PersistedOptions = {
|
|
|
88
88
|
targetDir: '/foo',
|
|
89
89
|
framework: 'test',
|
|
90
90
|
mode: 'code-router',
|
|
91
|
-
|
|
91
|
+
chosenAddOns: [],
|
|
92
92
|
version: 1,
|
|
93
93
|
typescript: true,
|
|
94
94
|
tailwind: true,
|
|
@@ -37,7 +37,7 @@ describe('writeConfigFile', () => {
|
|
|
37
37
|
const persistedOptions = {
|
|
38
38
|
version: 1,
|
|
39
39
|
framework: options.framework.id,
|
|
40
|
-
|
|
40
|
+
chosenAddOns: options.chosenAddOns.map((addOn) => addOn.id),
|
|
41
41
|
}
|
|
42
42
|
const env = {
|
|
43
43
|
writeFile: (path, optionsString) => {
|
|
@@ -55,7 +55,7 @@ describe('readConfigFileFromEnvironment', () => {
|
|
|
55
55
|
const persistedOptions = {
|
|
56
56
|
version: 1,
|
|
57
57
|
framework: 'react-cra',
|
|
58
|
-
|
|
58
|
+
chosenAddOns: ['add-on-1'],
|
|
59
59
|
}
|
|
60
60
|
const { environment } = createMemoryEnvironment()
|
|
61
61
|
environment.writeFile(
|
|
@@ -65,4 +65,41 @@ describe('readConfigFileFromEnvironment', () => {
|
|
|
65
65
|
const config = await readConfigFileFromEnvironment(environment, targetDir)
|
|
66
66
|
expect(config).toEqual(persistedOptions)
|
|
67
67
|
})
|
|
68
|
+
|
|
69
|
+
it('should upgrade old config files', async () => {
|
|
70
|
+
const targetDir = 'test-dir'
|
|
71
|
+
const persistedOptions = {
|
|
72
|
+
framework: 'react',
|
|
73
|
+
projectName: 'foo',
|
|
74
|
+
typescript: false,
|
|
75
|
+
tailwind: false,
|
|
76
|
+
packageManager: 'pnpm',
|
|
77
|
+
toolchain: 'none',
|
|
78
|
+
mode: 'code-router',
|
|
79
|
+
git: true,
|
|
80
|
+
variableValues: {},
|
|
81
|
+
version: 1,
|
|
82
|
+
existingAddOns: [],
|
|
83
|
+
}
|
|
84
|
+
const { output, environment } = createMemoryEnvironment()
|
|
85
|
+
environment.writeFile(
|
|
86
|
+
resolve(targetDir, CONFIG_FILE),
|
|
87
|
+
JSON.stringify(persistedOptions, null, 2),
|
|
88
|
+
)
|
|
89
|
+
const config = await readConfigFileFromEnvironment(environment, targetDir)
|
|
90
|
+
|
|
91
|
+
environment.finishRun()
|
|
92
|
+
|
|
93
|
+
expect(config).toEqual({
|
|
94
|
+
framework: 'react-cra',
|
|
95
|
+
projectName: 'foo',
|
|
96
|
+
typescript: false,
|
|
97
|
+
tailwind: false,
|
|
98
|
+
packageManager: 'pnpm',
|
|
99
|
+
chosenAddOns: [],
|
|
100
|
+
git: true,
|
|
101
|
+
mode: 'code-router',
|
|
102
|
+
version: 1,
|
|
103
|
+
})
|
|
104
|
+
})
|
|
68
105
|
})
|
|
@@ -84,7 +84,6 @@ describe('createAppOptionsFromPersisted', () => {
|
|
|
84
84
|
targetDir: '',
|
|
85
85
|
starter: undefined,
|
|
86
86
|
chosenAddOns: [],
|
|
87
|
-
existingAddOns: [],
|
|
88
87
|
version: 1,
|
|
89
88
|
} as PersistedOptions
|
|
90
89
|
const appOptions = await createAppOptionsFromPersisted(persistedOptions)
|
|
@@ -114,7 +113,6 @@ describe('createSerializedOptionsFromPersisted', () => {
|
|
|
114
113
|
targetDir: '',
|
|
115
114
|
starter: undefined,
|
|
116
115
|
chosenAddOns: [],
|
|
117
|
-
existingAddOns: [],
|
|
118
116
|
version: 1,
|
|
119
117
|
} as PersistedOptions
|
|
120
118
|
const appOptions =
|
|
@@ -205,14 +203,12 @@ describe('readCurrentProjectOptions', () => {
|
|
|
205
203
|
targetDir: '',
|
|
206
204
|
starter: undefined,
|
|
207
205
|
chosenAddOns: [],
|
|
208
|
-
existingAddOns: [],
|
|
209
206
|
version: 1,
|
|
210
207
|
}),
|
|
211
208
|
)
|
|
212
209
|
const options = await readCurrentProjectOptions(environment)
|
|
213
210
|
expect(options).toEqual({
|
|
214
211
|
chosenAddOns: [],
|
|
215
|
-
existingAddOns: [],
|
|
216
212
|
framework: 'test',
|
|
217
213
|
git: true,
|
|
218
214
|
mode: 'code-router',
|
|
@@ -15,13 +15,13 @@ describe('readOrGenerateStarterInfo', () => {
|
|
|
15
15
|
const starterInfo = await readOrGenerateStarterInfo({
|
|
16
16
|
framework: 'test',
|
|
17
17
|
version: 1,
|
|
18
|
-
existingAddOns: [],
|
|
19
18
|
starter: undefined,
|
|
20
19
|
projectName: 'test',
|
|
21
20
|
mode: 'code-router',
|
|
22
21
|
typescript: true,
|
|
23
22
|
tailwind: true,
|
|
24
23
|
git: true,
|
|
24
|
+
chosenAddOns: [],
|
|
25
25
|
})
|
|
26
26
|
expect(starterInfo.id).toEqual('test-starter')
|
|
27
27
|
})
|
|
@@ -33,7 +33,7 @@ describe('readOrGenerateStarterInfo', () => {
|
|
|
33
33
|
JSON.stringify({
|
|
34
34
|
framework: 'test',
|
|
35
35
|
version: 1,
|
|
36
|
-
|
|
36
|
+
chosenAddOns: [],
|
|
37
37
|
starter: undefined,
|
|
38
38
|
name: 'test-starter',
|
|
39
39
|
mode: 'code-router',
|
|
@@ -45,7 +45,7 @@ describe('readOrGenerateStarterInfo', () => {
|
|
|
45
45
|
const starterInfo = await readOrGenerateStarterInfo({
|
|
46
46
|
framework: 'test',
|
|
47
47
|
version: 1,
|
|
48
|
-
|
|
48
|
+
chosenAddOns: [],
|
|
49
49
|
starter: undefined,
|
|
50
50
|
projectName: 'test',
|
|
51
51
|
mode: 'code-router',
|