pokit 0.0.2 → 0.0.4

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 (3) hide show
  1. package/bin/pok.ts +182 -24
  2. package/package.json +5 -9
  3. package/src/init.ts +65 -0
package/bin/pok.ts CHANGED
@@ -2,41 +2,199 @@
2
2
  /**
3
3
  * pokit - Global CLI launcher for pok
4
4
  *
5
- * This is a thin wrapper that:
6
- * 1. Tries to import @pokit/core from the current project
7
- * 2. Calls runCli() to handle the actual CLI logic
8
- * 3. Shows helpful error messages if requirements are not met
5
+ * This is the global CLI entry point that:
6
+ * 1. Searches for pok.config.ts (or .config/pok.config.ts) starting from cwd
7
+ * 2. Loads and validates the config
8
+ * 3. Resolves paths relative to config file location
9
+ * 4. Dynamically imports adapters
10
+ * 5. Calls runCli() with resolved configuration
9
11
  *
10
12
  * Install globally with: bun add -g pokit
11
- * Then run `pok` from any project with @pokit/core installed.
13
+ * Then run `pok` from any project with a pok.config.ts file.
12
14
  */
13
15
 
16
+ import { resolve } from 'bun';
17
+ import * as fs from 'fs';
18
+ import * as path from 'path';
19
+
20
+ // Handle init before config discovery - must work without a config file
21
+ const args = process.argv.slice(2);
22
+ if (args[0] === 'init') {
23
+ const { runInit } = await import('../src/init');
24
+ await runInit();
25
+ process.exit(0);
26
+ }
27
+
28
+ /**
29
+ * Simple inline config file search (no external dependencies).
30
+ * Searches for pok.config.ts starting from startDir, walking up the tree.
31
+ */
32
+ function findConfigFileSimple(startDir: string): { configPath: string; configDir: string } | null {
33
+ let dir = startDir;
34
+
35
+ while (true) {
36
+ // Check for pok.config.ts in current directory
37
+ const configPath = path.join(dir, 'pok.config.ts');
38
+ if (fs.existsSync(configPath)) {
39
+ return { configPath, configDir: dir };
40
+ }
41
+
42
+ // Check for .config/pok.config.ts
43
+ const dotConfigPath = path.join(dir, '.config', 'pok.config.ts');
44
+ if (fs.existsSync(dotConfigPath)) {
45
+ return { configPath: dotConfigPath, configDir: dir };
46
+ }
47
+
48
+ // Move up to parent directory
49
+ const parentDir = path.dirname(dir);
50
+ if (parentDir === dir) {
51
+ // Reached filesystem root
52
+ return null;
53
+ }
54
+ dir = parentDir;
55
+ }
56
+ }
57
+
14
58
  async function main() {
59
+ const cwd = process.cwd();
60
+
61
+ // Step 1: Find config file using simple inline search
62
+ const configResult = findConfigFileSimple(cwd);
63
+
64
+ if (!configResult) {
65
+ console.error(`Error: No pok configuration found.
66
+
67
+ Run \`pok init\` to create a pok.config.ts file.
68
+ `);
69
+ process.exit(1);
70
+ }
71
+
72
+ const { configPath, configDir } = configResult;
73
+
74
+ // Step 2: Dynamically resolve @pokit/config from the project directory
75
+ let configModule: {
76
+ validateConfig: (config: unknown, configPath: string) => {
77
+ commandsDir: string;
78
+ projectRoot?: string;
79
+ appName?: string;
80
+ reporterAdapter: string;
81
+ prompter: string;
82
+ tabs?: string;
83
+ version?: string;
84
+ };
85
+ };
86
+
15
87
  try {
16
- const { runCli } = await import('@pokit/core');
17
- await runCli(process.argv.slice(2));
18
- } catch (error) {
19
- // Check if it's a module resolution error
20
- if (
21
- error instanceof Error &&
22
- (error.message.includes('Cannot find') ||
23
- error.message.includes('could not resolve') ||
24
- error.message.includes('Module not found'))
25
- ) {
88
+ const configModulePath = await resolve('@pokit/config', configDir);
89
+ configModule = await import(configModulePath);
90
+ } catch {
91
+ console.error(
92
+ `Error: @pokit/config is not installed in ${configDir}\n\n` +
93
+ 'Install it with:\n' +
94
+ ' bun add @pokit/config\n'
95
+ );
96
+ process.exit(1);
97
+ }
98
+
99
+ // Step 3: Load and validate config using the dynamically imported module
100
+ let config: ReturnType<typeof configModule.validateConfig>;
101
+ try {
102
+ const rawConfig = await import(configPath);
103
+ config = configModule.validateConfig(rawConfig.default, configPath);
104
+ } catch (err) {
105
+ const errorMessage = err instanceof Error ? err.message : String(err);
106
+ console.error(`Error: Failed to load config from ${configPath}\n`);
107
+ console.error(errorMessage);
108
+ process.exit(1);
109
+ }
110
+
111
+ // Step 4: Resolve paths relative to config file location
112
+ const commandsDir = path.resolve(configDir, config.commandsDir);
113
+ const projectRoot = config.projectRoot
114
+ ? path.resolve(configDir, config.projectRoot)
115
+ : configDir;
116
+
117
+ // Verify commands directory exists
118
+ if (!fs.existsSync(commandsDir)) {
119
+ console.error(`Error: Commands directory not found: ${commandsDir}\n`);
120
+ console.error(`The commandsDir path in ${configPath} resolves to a directory that doesn't exist.`);
121
+ process.exit(1);
122
+ }
123
+
124
+ // Step 5: Resolve @pokit/core from the config directory
125
+ let corePath: string;
126
+ try {
127
+ corePath = await resolve('@pokit/core', configDir);
128
+ } catch {
129
+ console.error(
130
+ `Error: @pokit/core is not installed in ${configDir}\n\n` +
131
+ 'Install it with:\n' +
132
+ ' bun add @pokit/core\n'
133
+ );
134
+ process.exit(1);
135
+ }
136
+
137
+ // Step 6: Dynamically import adapters from the config directory
138
+ let createReporterAdapter: (options?: { output?: unknown }) => unknown;
139
+ let createPrompter: () => unknown;
140
+ let createTabs: (() => unknown) | undefined;
141
+
142
+ // Import reporter adapter
143
+ try {
144
+ const reporterPath = await resolve(config.reporterAdapter, configDir);
145
+ const reporterModule = await import(reporterPath);
146
+ createReporterAdapter = reporterModule.createReporterAdapter;
147
+ } catch {
148
+ console.error(
149
+ `Error: Reporter adapter "${config.reporterAdapter}" is not installed.\n\n` +
150
+ `Install it with:\n` +
151
+ ` bun add ${config.reporterAdapter}\n`
152
+ );
153
+ process.exit(1);
154
+ }
155
+
156
+ // Import prompter
157
+ try {
158
+ const prompterPath = await resolve(config.prompter, configDir);
159
+ const prompterModule = await import(prompterPath);
160
+ createPrompter = prompterModule.createPrompter;
161
+ } catch {
162
+ console.error(
163
+ `Error: Prompter "${config.prompter}" is not installed.\n\n` +
164
+ `Install it with:\n` +
165
+ ` bun add ${config.prompter}\n`
166
+ );
167
+ process.exit(1);
168
+ }
169
+
170
+ // Import tabs adapter if configured
171
+ if (config.tabs) {
172
+ try {
173
+ const tabsPath = await resolve(config.tabs, configDir);
174
+ const tabsModule = await import(tabsPath);
175
+ createTabs = tabsModule.createTabs;
176
+ } catch {
26
177
  console.error(
27
- 'Error: @pokit/core is not installed in this project.\n\n' +
28
- 'Requirements:\n' +
29
- ' - Bun runtime: https://bun.sh\n' +
30
- ' - @pokit/core installed in your project\n\n' +
31
- 'Install with:\n' +
32
- ' bun add @pokit/core\n'
178
+ `Error: Tabs adapter "${config.tabs}" is not installed.\n\n` +
179
+ `Install it with:\n` +
180
+ ` bun add ${config.tabs}\n`
33
181
  );
34
182
  process.exit(1);
35
183
  }
36
-
37
- // Re-throw other errors
38
- throw error;
39
184
  }
185
+
186
+ // Step 7: Import core and call runCli
187
+ const { runCli } = await import(corePath);
188
+
189
+ await runCli(process.argv.slice(2), {
190
+ commandsDir,
191
+ projectRoot,
192
+ appName: config.appName,
193
+ version: config.version,
194
+ reporterAdapter: createReporterAdapter(),
195
+ prompter: createPrompter(),
196
+ tabs: createTabs?.(),
197
+ });
40
198
  }
41
199
 
42
200
  main().catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pokit",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Global CLI launcher for pok - install once, run anywhere",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -17,7 +17,8 @@
17
17
  "pok": "./bin/pok.ts"
18
18
  },
19
19
  "files": [
20
- "bin"
20
+ "bin",
21
+ "src"
21
22
  ],
22
23
  "publishConfig": {
23
24
  "access": "public"
@@ -32,10 +33,5 @@
32
33
  "cli",
33
34
  "pok",
34
35
  "pokit"
35
- ],
36
- "dependencies": {
37
- "@pokit/core": "^0.0.2",
38
- "@pokit/reporter-clack": "^0.0.2",
39
- "@pokit/prompter-clack": "^0.0.2"
40
- }
41
- }
36
+ ]
37
+ }
package/src/init.ts ADDED
@@ -0,0 +1,65 @@
1
+ /**
2
+ * pok init command
3
+ *
4
+ * Scaffolds a basic pok.config.ts file for new projects.
5
+ */
6
+
7
+ import { resolve } from 'bun';
8
+ import * as fs from 'fs';
9
+ import * as path from 'path';
10
+
11
+ const CONFIG_FILENAME = 'pok.config.ts';
12
+
13
+ /**
14
+ * Fallback template used when @pokit/config isn't installed yet.
15
+ * This enables bootstrapping new projects.
16
+ */
17
+ const FALLBACK_CONFIG_TEMPLATE = `import { defineConfig } from 'pokit'
18
+
19
+ export default defineConfig({
20
+ commandsDir: './commands',
21
+ reporterAdapter: '@pokit/reporter-clack',
22
+ prompter: '@pokit/prompter-clack',
23
+ })
24
+ `;
25
+
26
+ /**
27
+ * Try to get CONFIG_TEMPLATE from @pokit/config, falling back to hardcoded template.
28
+ */
29
+ async function getConfigTemplate(cwd: string): Promise<string> {
30
+ try {
31
+ const configModulePath = await resolve('@pokit/config', cwd);
32
+ const configModule = await import(configModulePath);
33
+ return configModule.CONFIG_TEMPLATE ?? FALLBACK_CONFIG_TEMPLATE;
34
+ } catch {
35
+ // @pokit/config not installed yet - use fallback for bootstrapping
36
+ return FALLBACK_CONFIG_TEMPLATE;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Run the init command to create a pok.config.ts file
42
+ */
43
+ export async function runInit(): Promise<void> {
44
+ const cwd = process.cwd();
45
+ const configPath = path.join(cwd, CONFIG_FILENAME);
46
+
47
+ // Check if config already exists
48
+ if (fs.existsSync(configPath)) {
49
+ console.error('Error: pok.config.ts already exists in this directory.');
50
+ process.exit(1);
51
+ }
52
+
53
+ // Get the config template (from @pokit/config or fallback)
54
+ const template = await getConfigTemplate(cwd);
55
+
56
+ // Write the config file
57
+ fs.writeFileSync(configPath, template);
58
+
59
+ console.log(`Created pok.config.ts
60
+
61
+ Next steps:
62
+ 1. Create a commands/ directory
63
+ 2. Add your first command file
64
+ 3. Run \`pok\` to see available commands`);
65
+ }