create-foldkit-app 0.19.0 → 0.20.0
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/utils/packages.js +79 -25
- package/package.json +7 -4
- package/templates/base/.oxlintrc.json +2 -1
package/dist/utils/packages.js
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
import { Array, Effect, Match, Record, Schema, pipe } from 'effect';
|
|
1
|
+
import { Array, Effect, FileSystem, Match, Order, Path, Record, Result, Schema, pipe, } from 'effect';
|
|
2
2
|
import { HttpClient, HttpClientRequest } from 'effect/unstable/http';
|
|
3
3
|
import { spawn } from 'node:child_process';
|
|
4
4
|
const GITHUB_RAW_BASE_URL = 'https://raw.githubusercontent.com/foldkit/foldkit/main/examples';
|
|
5
|
+
const NPM_REGISTRY_BASE_URL = 'https://registry.npmjs.org';
|
|
6
|
+
const FOLDKIT_SCOPE_PREFIX = '@foldkit/';
|
|
5
7
|
const isWindows = process.platform === 'win32';
|
|
6
|
-
const getInstallArgs = (packageManager, isDev = false) => pipe(Match.value(packageManager), Match.when('npm', () => ['install']), Match.when('yarn', () => ['add']), Match.when('pnpm', () => ['add']), Match.when('bun', () => ['add']), Match.exhaustive, args => (isDev ? [...args, '-D'] : args));
|
|
7
8
|
const StringRecord = Schema.Record(Schema.String, Schema.String);
|
|
8
9
|
const PackageJson = Schema.Struct({
|
|
9
10
|
dependencies: StringRecord.pipe(Schema.withDecodingDefaultKey(Effect.succeed({}))),
|
|
10
11
|
devDependencies: StringRecord.pipe(Schema.withDecodingDefaultKey(Effect.succeed({}))),
|
|
11
12
|
});
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
const ProjectPackageJson = Schema.Struct({
|
|
14
|
+
name: Schema.String,
|
|
15
|
+
version: Schema.String,
|
|
16
|
+
type: Schema.String,
|
|
17
|
+
scripts: StringRecord,
|
|
18
|
+
});
|
|
19
|
+
const NpmPackument = Schema.Struct({
|
|
20
|
+
version: Schema.String,
|
|
21
|
+
});
|
|
19
22
|
const TEMPLATE_DEV_DEPENDENCIES = [
|
|
20
23
|
'@foldkit/vite-plugin',
|
|
21
24
|
'@foldkit/devtools-mcp',
|
|
@@ -26,18 +29,73 @@ const TEMPLATE_DEV_DEPENDENCIES = [
|
|
|
26
29
|
'prettier',
|
|
27
30
|
'vitest',
|
|
28
31
|
];
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
+
const isFoldkitPackage = (name) => name === 'foldkit' || name.startsWith(FOLDKIT_SCOPE_PREFIX);
|
|
33
|
+
const Keep = (version) => ({ _tag: 'Keep', version });
|
|
34
|
+
const Latest = { _tag: 'Latest' };
|
|
35
|
+
const toUnresolvedSpec = (spec, name) => {
|
|
36
|
+
if (spec.includes('workspace:')) {
|
|
37
|
+
return isFoldkitPackage(name) ? Result.succeed(Latest) : Result.failVoid;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
return Result.succeed(Keep(spec));
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Build the runtime dependency map for a scaffolded project from an example's
|
|
45
|
+
* raw `dependencies`. Concrete versions are kept, Foldkit monorepo packages are
|
|
46
|
+
* marked for latest-version resolution, and any other workspace packages (which
|
|
47
|
+
* are not published) are dropped.
|
|
48
|
+
*/
|
|
49
|
+
export const buildUnresolvedDeps = (exampleDeps) => Record.filterMap(exampleDeps, toUnresolvedSpec);
|
|
50
|
+
/**
|
|
51
|
+
* Build the devDependency map for a scaffolded project by merging the always-on
|
|
52
|
+
* template tooling with the example's own `devDependencies`. A concrete version
|
|
53
|
+
* from the example wins over the template's latest marker for the same package.
|
|
54
|
+
*/
|
|
55
|
+
export const buildUnresolvedDevDeps = (exampleDevDeps) => {
|
|
56
|
+
const templateSpecs = Record.fromIterableWith(TEMPLATE_DEV_DEPENDENCIES, name => [name, Latest]);
|
|
57
|
+
const exampleSpecs = Record.filterMap(exampleDevDeps, toUnresolvedSpec);
|
|
58
|
+
return Record.union(templateSpecs, exampleSpecs, (_templateSpec, exampleSpec) => exampleSpec);
|
|
59
|
+
};
|
|
60
|
+
const resolveLatestVersion = (name) => Effect.gen(function* () {
|
|
61
|
+
const client = yield* HttpClient.HttpClient;
|
|
62
|
+
const encodedName = name.replace('/', '%2F');
|
|
63
|
+
const url = `${NPM_REGISTRY_BASE_URL}/${encodedName}/latest`;
|
|
64
|
+
const response = yield* client.execute(HttpClientRequest.get(url));
|
|
65
|
+
const json = yield* response.json;
|
|
66
|
+
const packument = yield* Schema.decodeUnknownEffect(NpmPackument)(json);
|
|
67
|
+
return `^${packument.version}`;
|
|
68
|
+
});
|
|
69
|
+
const resolveEntry = (name, spec) => Match.value(spec).pipe(Match.tagsExhaustive({
|
|
70
|
+
Keep: ({ version }) => Effect.succeed([name, version]),
|
|
71
|
+
Latest: () => Effect.map(resolveLatestVersion(name), version => [name, version]),
|
|
72
|
+
}));
|
|
73
|
+
const resolveSpecs = (unresolved) => Effect.gen(function* () {
|
|
74
|
+
const entries = Record.toEntries(unresolved);
|
|
75
|
+
const resolved = yield* Effect.forEach(entries, ([name, spec]) => resolveEntry(name, spec), { concurrency: 'unbounded' });
|
|
76
|
+
return Record.fromEntries(resolved);
|
|
77
|
+
});
|
|
78
|
+
const byPackageName = Order.mapInput(Order.String, ([name]) => name);
|
|
79
|
+
const sortDependencies = (dependencies) => pipe(dependencies, Record.toEntries, Array.sort(byPackageName), Record.fromEntries);
|
|
80
|
+
const fetchExamplePackageJson = (example) => Effect.gen(function* () {
|
|
32
81
|
const client = yield* HttpClient.HttpClient;
|
|
33
82
|
const url = `${GITHUB_RAW_BASE_URL}/${example}/package.json`;
|
|
34
83
|
const response = yield* client.execute(HttpClientRequest.get(url));
|
|
35
84
|
const json = yield* response.json;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
85
|
+
return yield* Schema.decodeUnknownEffect(PackageJson)(json);
|
|
86
|
+
});
|
|
87
|
+
const writeManifest = (projectPath, dependencies, devDependencies) => Effect.gen(function* () {
|
|
88
|
+
const fs = yield* FileSystem.FileSystem;
|
|
89
|
+
const path = yield* Path.Path;
|
|
90
|
+
const packageJsonPath = path.join(projectPath, 'package.json');
|
|
91
|
+
const content = yield* fs.readFileString(packageJsonPath);
|
|
92
|
+
const packageJson = yield* Schema.decodeUnknownEffect(ProjectPackageJson)(JSON.parse(content));
|
|
93
|
+
const updated = {
|
|
94
|
+
...packageJson,
|
|
95
|
+
dependencies,
|
|
96
|
+
devDependencies,
|
|
40
97
|
};
|
|
98
|
+
yield* fs.writeFileString(packageJsonPath, `${JSON.stringify(updated, null, 2)}\n`);
|
|
41
99
|
});
|
|
42
100
|
const runCommand = (command, args, cwd) => Effect.callback((resume) => {
|
|
43
101
|
const child = spawn(command, [...args], {
|
|
@@ -64,13 +122,9 @@ const runCommand = (command, args, cwd) => Effect.callback((resume) => {
|
|
|
64
122
|
});
|
|
65
123
|
});
|
|
66
124
|
export const installDependencies = (projectPath, packageManager, example) => Effect.gen(function* () {
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
yield*
|
|
70
|
-
|
|
71
|
-
yield* runCommand(packageManager, [
|
|
72
|
-
...installDevArgs,
|
|
73
|
-
...TEMPLATE_DEV_DEPENDENCIES,
|
|
74
|
-
...exampleDeps.devDependencies,
|
|
75
|
-
], projectPath);
|
|
125
|
+
const examplePackageJson = yield* fetchExamplePackageJson(example);
|
|
126
|
+
const dependencies = yield* resolveSpecs(buildUnresolvedDeps(examplePackageJson.dependencies));
|
|
127
|
+
const devDependencies = yield* resolveSpecs(buildUnresolvedDevDeps(examplePackageJson.devDependencies));
|
|
128
|
+
yield* writeManifest(projectPath, sortDependencies(dependencies), sortDependencies(devDependencies));
|
|
129
|
+
yield* runCommand(packageManager, ['install'], projectPath);
|
|
76
130
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-foldkit-app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Create Foldkit applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
"typescript": "^6.0.3"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@
|
|
23
|
+
"@effect/vitest": "4.0.0-beta.88",
|
|
24
|
+
"@types/node": "^25.9.3",
|
|
25
|
+
"vitest": "^4.1.9"
|
|
24
26
|
},
|
|
25
27
|
"keywords": [
|
|
26
28
|
"create-foldkit-app",
|
|
@@ -45,8 +47,9 @@
|
|
|
45
47
|
},
|
|
46
48
|
"scripts": {
|
|
47
49
|
"clean": "rimraf dist *.tsbuildinfo",
|
|
48
|
-
"build": "pnpm run clean && tsc -b",
|
|
50
|
+
"build": "pnpm run clean && tsc -b tsconfig.build.json",
|
|
49
51
|
"format": "prettier -w .",
|
|
50
|
-
"
|
|
52
|
+
"test": "vitest run",
|
|
53
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
51
54
|
}
|
|
52
55
|
}
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"foldkit/got-prefix-requires-submodel-payload": "error",
|
|
32
32
|
"foldkit/no-empty-object-tagged-call": "error",
|
|
33
33
|
"foldkit/prefer-callable-message-constructor": "error",
|
|
34
|
-
"foldkit/command-binding-matches-name": "error"
|
|
34
|
+
"foldkit/command-binding-matches-name": "error",
|
|
35
|
+
"foldkit/no-module-level-mutable-state": "error"
|
|
35
36
|
},
|
|
36
37
|
"ignorePatterns": [
|
|
37
38
|
"dist/",
|