create-foldkit-app 0.17.0 → 0.17.2

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/README.md CHANGED
@@ -18,22 +18,33 @@ The CLI prompts you for a project name, starter example, and package manager. Pa
18
18
 
19
19
  ## Examples
20
20
 
21
- | Example | Description |
22
- | ---------------- | ----------------------------------------------------------------------------------- |
23
- | `counter` | Simple increment/decrement with reset |
24
- | `counters` | A dynamic list of Counter Submodels with per-instance routing via a wrapper Message |
25
- | `stopwatch` | Timer with start/stop/reset |
26
- | `weather` | HTTP requests with async state handling |
27
- | `todo` | CRUD operations with localStorage persistence |
28
- | `form` | Form validation with async email checking |
29
- | `snake` | Classic game built with Subscriptions |
30
- | `routing` | URL routing with parser combinators and route parameters |
31
- | `query-sync` | URL-driven filtering, sorting, and search |
32
- | `shopping-cart` | Complex state management with nested Models |
33
- | `websocket-chat` | Managed resources with WebSocket |
34
- | `auth` | Authentication with Submodels, OutMessage, and protected routes |
35
- | `embedding` | A Foldkit widget embedded in a plain TypeScript host page with Ports and dispose |
36
- | `ui-showcase` | Every Foldkit UI component with routing |
21
+ | Example | Description |
22
+ | ------------------------ | ------------------------------------------------------------------------------------------------------ |
23
+ | `counter` | Simple increment/decrement with reset |
24
+ | `counters` | A dynamic list of Counter Submodels with per-instance routing via a wrapper Message |
25
+ | `todo` | CRUD operations with localStorage persistence |
26
+ | `stopwatch` | Timer with start/stop/reset |
27
+ | `crash-view` | Custom crash fallback UI with crash.view and crash.report |
28
+ | `slow-warnings` | Interactive lab for slow update, view, patch, and Subscription dependency warnings |
29
+ | `form` | Form validation with async email checking |
30
+ | `job-application` | Multi-step form with async validation, file uploads, and per-step error indicators |
31
+ | `weather` | HTTP requests with async state handling |
32
+ | `api-cache` | Query caching in the Model with stale-while-revalidate, request deduplication, and interval refetching |
33
+ | `routing` | URL routing with parser combinators and route parameters |
34
+ | `query-sync` | URL-driven filtering, sorting, and search with query parameters |
35
+ | `snake` | Classic game built with Subscriptions |
36
+ | `canvas-art` | Declarative 2D canvas with shapes, animation-frame Subscriptions, and pointer events |
37
+ | `generative-art` | Perlin-noise flow field with evolving particle trails, mouse vortex, and high-frequency Messages |
38
+ | `auth` | Authentication with Submodels, OutMessage, and protected routes |
39
+ | `shopping-cart` | Complex state management with nested Models and routing |
40
+ | `pixel-art` | Pixel editor with undo/redo, UI components, and localStorage persistence |
41
+ | `websocket-chat` | Managed resources with WebSocket integration |
42
+ | `managed-resource-layer` | Layer-backed ManagedResource lifecycle with an Effect service |
43
+ | `kanban` | Drag-and-drop board with fractional indexing, keyboard navigation, and screen reader announcements |
44
+ | `map` | Interactive MapLibre GL map with Mount and Subscriptions |
45
+ | `web-components` | QR code designer wiring third-party web components into Foldkit with CustomElement.define |
46
+ | `embedding` | A Foldkit widget embedded in a plain TypeScript host page with Ports and dispose |
47
+ | `ui-showcase` | Every Foldkit UI component with routing and Submodels |
37
48
 
38
49
  ## License
39
50
 
@@ -60,10 +60,10 @@ const validateProject = (name, projectPath, packageManager) => Effect.gen(functi
60
60
  return yield* Effect.fail(`Package manager '${packageManager}' is not available. Please install it first.`);
61
61
  }
62
62
  });
63
- const setupProject = (name, projectPath, example) => Effect.gen(function* () {
63
+ const setupProject = (name, projectPath, example, packageManager) => Effect.gen(function* () {
64
64
  yield* Console.log(chalk.blue('🚀 Creating your Foldkit app...'));
65
65
  yield* Console.log('');
66
- yield* createProject(name, projectPath, example);
66
+ yield* createProject(name, projectPath, example, packageManager);
67
67
  yield* Console.log(chalk.green(`✅ Created project`));
68
68
  yield* Console.log('');
69
69
  });
@@ -118,7 +118,7 @@ export const create = (input) => Effect.gen(function* () {
118
118
  const path = yield* Path.Path;
119
119
  const projectPath = path.resolve(name);
120
120
  yield* validateProject(name, projectPath, packageManager);
121
- yield* setupProject(name, projectPath, example);
121
+ yield* setupProject(name, projectPath, example, packageManager);
122
122
  yield* installProjectDependencies(projectPath, packageManager, example);
123
123
  yield* displaySuccessMessage(name, packageManager);
124
124
  return name;
package/dist/examples.js CHANGED
@@ -4,6 +4,7 @@ export const EXAMPLE_VALUES = [
4
4
  'todo',
5
5
  'stopwatch',
6
6
  'crash-view',
7
+ 'slow-warnings',
7
8
  'form',
8
9
  'job-application',
9
10
  'weather',
@@ -17,7 +18,9 @@ export const EXAMPLE_VALUES = [
17
18
  'shopping-cart',
18
19
  'pixel-art',
19
20
  'websocket-chat',
21
+ 'managed-resource-layer',
20
22
  'kanban',
23
+ 'map',
21
24
  'web-components',
22
25
  'embedding',
23
26
  'ui-showcase',
@@ -48,6 +51,11 @@ export const examples = [
48
51
  title: 'crash-view',
49
52
  description: 'Custom crash fallback UI with crash.view and crash.report',
50
53
  },
54
+ {
55
+ value: 'slow-warnings',
56
+ title: 'slow-warnings',
57
+ description: 'Interactive lab for slow update, view, patch, and Subscription dependency warnings',
58
+ },
51
59
  {
52
60
  value: 'form',
53
61
  title: 'form',
@@ -113,11 +121,21 @@ export const examples = [
113
121
  title: 'websocket-chat',
114
122
  description: 'Managed resources with WebSocket integration',
115
123
  },
124
+ {
125
+ value: 'managed-resource-layer',
126
+ title: 'managed-resource-layer',
127
+ description: 'Layer-backed ManagedResource lifecycle with an Effect service',
128
+ },
116
129
  {
117
130
  value: 'kanban',
118
131
  title: 'kanban',
119
132
  description: 'Drag-and-drop board with fractional indexing, keyboard navigation, and screen reader announcements',
120
133
  },
134
+ {
135
+ value: 'map',
136
+ title: 'map',
137
+ description: 'Interactive MapLibre GL map with Mount and Subscriptions',
138
+ },
121
139
  {
122
140
  value: 'web-components',
123
141
  title: 'web-components',
@@ -2,18 +2,21 @@ import { Array, Effect, FileSystem, Match, Option, Path, Record, Ref, Schema, St
2
2
  import { HttpClient, HttpClientRequest, } from 'effect/unstable/http';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  const GITHUB_API_BASE_URL = 'https://api.github.com/repos/foldkit/foldkit/contents/examples';
5
- const getBaseFiles = Effect.gen(function* () {
6
- const fs = yield* FileSystem.FileSystem;
5
+ const getTemplateRoot = Effect.gen(function* () {
7
6
  const path = yield* Path.Path;
8
7
  const currentDir = path.dirname(fileURLToPath(import.meta.url));
9
- const templatesDir = path.resolve(currentDir, '..', '..', 'templates', 'base');
8
+ return path.resolve(currentDir, '..', '..', 'templates');
9
+ });
10
+ const getTemplateFiles = (templateDir) => Effect.gen(function* () {
11
+ const fs = yield* FileSystem.FileSystem;
12
+ const path = yield* Path.Path;
10
13
  const fileContentByPath = yield* Ref.make({});
11
14
  const processEntry = (dir) => (entry) => Effect.gen(function* () {
12
15
  const fullPath = path.join(dir, entry);
13
16
  const stat = yield* fs.stat(fullPath);
14
17
  yield* Match.value(stat.type).pipe(Match.when('Directory', () => processDirectory(fullPath)), Match.when('File', () => Effect.gen(function* () {
15
18
  const content = yield* fs.readFileString(fullPath);
16
- const relativePath = path.relative(templatesDir, fullPath);
19
+ const relativePath = path.relative(templateDir, fullPath);
17
20
  yield* Ref.update(fileContentByPath, files => ({
18
21
  ...files,
19
22
  [relativePath]: content,
@@ -26,20 +29,31 @@ const getBaseFiles = Effect.gen(function* () {
26
29
  concurrency: 'unbounded',
27
30
  });
28
31
  });
29
- yield* processDirectory(templatesDir);
32
+ yield* processDirectory(templateDir);
30
33
  return yield* Ref.get(fileContentByPath);
31
34
  });
32
- export const createProject = (name, projectPath, example) => Effect.gen(function* () {
33
- yield* createBaseFiles(projectPath);
34
- yield* modifyBaseFiles(projectPath, name);
35
- yield* createExampleFiles(projectPath, example);
35
+ const getBaseFiles = Effect.gen(function* () {
36
+ const path = yield* Path.Path;
37
+ const templateRoot = yield* getTemplateRoot;
38
+ return yield* getTemplateFiles(path.join(templateRoot, 'base'));
36
39
  });
37
- const createBaseFiles = (projectPath) => Effect.gen(function* () {
40
+ const getPackageManagerFiles = (packageManager) => Effect.gen(function* () {
38
41
  const fs = yield* FileSystem.FileSystem;
39
42
  const path = yield* Path.Path;
40
- yield* fs.makeDirectory(projectPath, { recursive: true });
41
- const baseFiles = yield* getBaseFiles;
42
- yield* pipe(baseFiles, Record.toEntries, Effect.forEach(([filePath, content]) => Effect.gen(function* () {
43
+ const templateRoot = yield* getTemplateRoot;
44
+ const templateDir = path.join(templateRoot, 'package-managers', packageManager);
45
+ const isTemplateDirectoryPresent = yield* fs.exists(templateDir);
46
+ if (isTemplateDirectoryPresent) {
47
+ return yield* getTemplateFiles(templateDir);
48
+ }
49
+ else {
50
+ return {};
51
+ }
52
+ });
53
+ const createFiles = (projectPath, files) => Effect.gen(function* () {
54
+ const fs = yield* FileSystem.FileSystem;
55
+ const path = yield* Path.Path;
56
+ yield* pipe(files, Record.toEntries, Effect.forEach(([filePath, content]) => Effect.gen(function* () {
43
57
  const targetPath = Match.value(filePath).pipe(Match.when('gitignore', () => '.gitignore'), Match.when('ignore', () => '.ignore'), Match.orElse(() => filePath));
44
58
  const fullPath = path.join(projectPath, targetPath);
45
59
  const dirPath = path.dirname(fullPath);
@@ -47,11 +61,29 @@ const createBaseFiles = (projectPath) => Effect.gen(function* () {
47
61
  yield* fs.writeFileString(fullPath, content);
48
62
  }), { concurrency: 'unbounded' }));
49
63
  });
64
+ const createBaseFiles = (projectPath) => Effect.gen(function* () {
65
+ const fs = yield* FileSystem.FileSystem;
66
+ yield* fs.makeDirectory(projectPath, { recursive: true });
67
+ const baseFiles = yield* getBaseFiles;
68
+ yield* createFiles(projectPath, baseFiles);
69
+ });
70
+ const createPackageManagerFiles = (projectPath, packageManager) => Effect.gen(function* () {
71
+ const packageManagerFiles = yield* getPackageManagerFiles(packageManager);
72
+ yield* createFiles(projectPath, packageManagerFiles);
73
+ });
74
+ export const createProject = (name, projectPath, example, packageManager) => Effect.gen(function* () {
75
+ yield* createBaseFiles(projectPath);
76
+ yield* modifyBaseFiles(projectPath, name);
77
+ yield* createPackageManagerFiles(projectPath, packageManager);
78
+ yield* createExampleFiles(projectPath, example);
79
+ });
50
80
  const modifyBaseFiles = (projectPath, name) => Effect.gen(function* () {
51
81
  const fs = yield* FileSystem.FileSystem;
52
82
  const path = yield* Path.Path;
53
83
  const packageJsonPath = path.join(projectPath, 'package.json');
54
- return yield* fs.readFileString(packageJsonPath).pipe(Effect.map(String.replace('my-foldkit-app', name)), Effect.flatMap(updatedContent => fs.writeFileString(packageJsonPath, updatedContent)));
84
+ const content = yield* fs.readFileString(packageJsonPath);
85
+ const updatedContent = String.replace('my-foldkit-app', name)(content);
86
+ yield* fs.writeFileString(packageJsonPath, updatedContent);
55
87
  });
56
88
  const GitHubFileEntry = Schema.Struct({
57
89
  name: Schema.String,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-foldkit-app",
3
- "version": "0.17.0",
3
+ "version": "0.17.2",
4
4
  "description": "Create Foldkit applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -0,0 +1,2 @@
1
+ allowBuilds:
2
+ msgpackr-extract: false