create-lib-workspace 1.0.0 → 1.1.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/README.md +2 -0
- package/index.js +215 -149
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@ A lightweight CLI tool to instantly scaffold a local development monorepo worksp
|
|
|
4
4
|
|
|
5
5
|
It automatically sets up a multi-entry library with proper ESM/CJS exports and links it to a static test application using your favorite package manager—completely dependency-free.
|
|
6
6
|
|
|
7
|
+

|
|
8
|
+
|
|
7
9
|
## Installation
|
|
8
10
|
|
|
9
11
|
You can install this tool globally to make it available anywhere on your system, or run it directly without installation.
|
package/index.js
CHANGED
|
@@ -7,59 +7,70 @@ import readline from 'readline';
|
|
|
7
7
|
|
|
8
8
|
// Helper to create an interactive terminal prompt using built-in readline
|
|
9
9
|
function askQuestion(query) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
const rl = readline.createInterface({
|
|
11
|
+
input: process.stdin,
|
|
12
|
+
output: process.stdout,
|
|
13
|
+
});
|
|
14
|
+
return new Promise(resolve =>
|
|
15
|
+
rl.question(query, ans => {
|
|
16
|
+
rl.close();
|
|
17
|
+
resolve(ans.trim());
|
|
18
|
+
})
|
|
19
|
+
);
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
async function main() {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// Parse inline arguments
|
|
24
|
-
const args = process.argv.slice(2);
|
|
25
|
-
let workspaceName = args[0];
|
|
26
|
-
let libName = args[1];
|
|
27
|
-
let packageName = args[2];
|
|
28
|
-
let pkgManager = args[3];
|
|
29
|
-
|
|
30
|
-
// Fallback to interactive prompts if arguments are missing
|
|
31
|
-
if (!workspaceName) {
|
|
32
|
-
workspaceName = await askQuestion('Enter the workspace directory name (e.g. my-workspace): ');
|
|
33
|
-
if (!workspaceName) { console.log('❌ Workspace name is required!'); process.exit(1); }
|
|
34
|
-
}
|
|
23
|
+
console.log('\n📦 Welcome to create-lib-workspace\n');
|
|
35
24
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
25
|
+
// Parse inline arguments
|
|
26
|
+
const args = process.argv.slice(2);
|
|
27
|
+
let workspaceName = args[0];
|
|
28
|
+
let libName = args[1];
|
|
29
|
+
let packageName = args[2];
|
|
30
|
+
let pkgManager = args[3];
|
|
40
31
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
32
|
+
// Fallback to interactive prompts if arguments are missing
|
|
33
|
+
if (!workspaceName) {
|
|
34
|
+
workspaceName = await askQuestion('Enter the workspace directory name (e.g. my-workspace): ');
|
|
35
|
+
if (!workspaceName) {
|
|
36
|
+
console.log('❌ Workspace name is required!');
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
45
40
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
41
|
+
if (!libName) {
|
|
42
|
+
libName = await askQuestion('Enter the library directory name (e.g. core-lib): ');
|
|
43
|
+
if (!libName) {
|
|
44
|
+
console.log('❌ Library name is required!');
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!packageName) {
|
|
50
|
+
packageName = await askQuestion('Enter the npm package name (e.g. @my-org/core): ');
|
|
51
|
+
if (!packageName) {
|
|
52
|
+
console.log('❌ Package name is required!');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
53
56
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
if (!pkgManager) {
|
|
58
|
+
console.log('\nSelect package manager:\n1) pnpm (Default)\n2) npm\n3) yarn');
|
|
59
|
+
const choice = await askQuestion('Enter number [1-3]: ');
|
|
60
|
+
if (choice === '2') pkgManager = 'npm';
|
|
61
|
+
else if (choice === '3') pkgManager = 'yarn';
|
|
62
|
+
else pkgManager = 'pnpm';
|
|
63
|
+
}
|
|
57
64
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
const workspacePath = path.resolve(workspaceName);
|
|
66
|
+
const appPath = path.join(workspacePath, 'app');
|
|
67
|
+
const libPath = path.join(workspacePath, libName);
|
|
61
68
|
|
|
62
|
-
|
|
69
|
+
// 1. Create Workspace & App Structure
|
|
70
|
+
console.log('\n[1/5] Creating workspace and consumer application...');
|
|
71
|
+
fs.mkdirSync(appPath, { recursive: true });
|
|
72
|
+
|
|
73
|
+
const htmlContent = `<!doctype html>
|
|
63
74
|
<html>
|
|
64
75
|
<head>
|
|
65
76
|
<meta charset="UTF-8" />
|
|
@@ -71,7 +82,7 @@ async function main() {
|
|
|
71
82
|
</body>
|
|
72
83
|
</html>`;
|
|
73
84
|
|
|
74
|
-
|
|
85
|
+
const mainJsContent = `import { pluck } from "${packageName}";
|
|
75
86
|
import { increaseAllOf, lowerCaseAllOf } from "${packageName}/modifiers";
|
|
76
87
|
|
|
77
88
|
const users = [
|
|
@@ -84,71 +95,124 @@ console.log("Plucked Names:", pluck(users, 'name'));
|
|
|
84
95
|
console.log("Increased Age:", increaseAllOf(users, 'age'));
|
|
85
96
|
console.log("Lowercased Names:", lowerCaseAllOf(users, 'name'));`;
|
|
86
97
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
// 2. Run Vite Scaffolding
|
|
91
|
-
console.log('\n---------------------------------------------------------');
|
|
92
|
-
console.log('⚠️ ATTENTION / IMPORTANT NOTE:');
|
|
93
|
-
console.log('Vite will now prompt you to target a framework and a variant.');
|
|
94
|
-
console.log('1. Please SELECT "Vanilla" and "JavaScript".');
|
|
95
|
-
console.log('2. ❌ DO NOT let it install dependencies or run immediately!');
|
|
96
|
-
console.log('---------------------------------------------------------\n');
|
|
97
|
-
|
|
98
|
-
try {
|
|
99
|
-
execSync(`${pkgManager} create vite ${libName} --template vanilla`, {
|
|
100
|
-
cwd: workspacePath,
|
|
101
|
-
stdio: 'inherit',
|
|
102
|
-
});
|
|
103
|
-
} catch (error) {
|
|
104
|
-
console.log('❌ Vite scaffolding failed or was aborted.');
|
|
105
|
-
process.exit(1);
|
|
106
|
-
}
|
|
98
|
+
fs.writeFileSync(path.join(appPath, 'index.html'), htmlContent);
|
|
99
|
+
fs.writeFileSync(path.join(appPath, 'main.js'), mainJsContent);
|
|
107
100
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
101
|
+
// 2. Run Vite Scaffolding
|
|
102
|
+
console.log('\n---------------------------------------------------------');
|
|
103
|
+
console.log('⚠️ ATTENTION / IMPORTANT NOTE:');
|
|
104
|
+
console.log('Vite will now prompt you to target a framework and a variant.');
|
|
105
|
+
console.log('1. Please SELECT "Vanilla" and "JavaScript".');
|
|
106
|
+
console.log('2. ❌ DO NOT let it install dependencies or run immediately!');
|
|
107
|
+
console.log('---------------------------------------------------------\n');
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
execSync(`${pkgManager} create vite ${libName} --template vanilla`, {
|
|
111
|
+
cwd: workspacePath,
|
|
112
|
+
stdio: 'inherit',
|
|
113
|
+
});
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.log('❌ Vite scaffolding failed or was aborted.');
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 3. Clean up Vite Boilerplate & Generate Library Sources
|
|
120
|
+
console.log('\n[2/5] Polishing library structure and writing source files...');
|
|
121
|
+
fs.rmSync(path.join(libPath, 'src'), { recursive: true, force: true });
|
|
122
|
+
fs.mkdirSync(path.join(libPath, 'src/modifiers'), { recursive: true });
|
|
123
|
+
|
|
124
|
+
// Dynamically fetch the absolute latest Vite and Vitest versions from registry
|
|
125
|
+
let viteVersion = '5.0.0';
|
|
126
|
+
let vitestVersion = '1.0.0';
|
|
127
|
+
try {
|
|
128
|
+
viteVersion = execSync('npm info vite version', { encoding: 'utf8' }).trim();
|
|
129
|
+
vitestVersion = execSync('npm info vitest version', { encoding: 'utf8' }).trim();
|
|
130
|
+
} catch (e) {
|
|
131
|
+
// Graceful fallback if no network
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Write source files
|
|
135
|
+
fs.writeFileSync(
|
|
136
|
+
path.join(libPath, 'src/index.js'),
|
|
137
|
+
`export function pluck(collection, field) {\n return collection.map(item => item[field]);\n}`
|
|
138
|
+
);
|
|
139
|
+
fs.writeFileSync(
|
|
140
|
+
path.join(libPath, 'src/modifiers/increaseAll.js'),
|
|
141
|
+
`export function increaseAllOf(collection, field) {\n return collection.map(item => ({\n ...item,\n [field]: item[field] + 1\n }));\n}`
|
|
142
|
+
);
|
|
143
|
+
fs.writeFileSync(
|
|
144
|
+
path.join(libPath, 'src/modifiers/lowerCaseAll.js'),
|
|
145
|
+
`export function lowerCaseAllOf(collection, field) {\n return collection.map(item => ({\n ...item,\n [field]: String(item[field]).toLowerCase()\n }));\n}`
|
|
146
|
+
);
|
|
147
|
+
fs.writeFileSync(
|
|
148
|
+
path.join(libPath, 'src/modifiers/index.js'),
|
|
149
|
+
`import { increaseAllOf } from "./increaseAll.js";\nimport { lowerCaseAllOf } from "./lowerCaseAll.js";\n\nexport { increaseAllOf, lowerCaseAllOf };`
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
// NEW: Add a comprehensive Vitest test file matching the example code
|
|
153
|
+
const testContent = `import { describe, it, expect } from "vitest";
|
|
154
|
+
import { pluck } from "./index.js";
|
|
155
|
+
import { increaseAllOf, lowerCaseAllOf } from "./modifiers/index.js";
|
|
156
|
+
|
|
157
|
+
describe("Library Core & Modifiers Stresstest", () => {
|
|
158
|
+
const mockUsers = [
|
|
159
|
+
{ name: 'Alice', age: 20 },
|
|
160
|
+
{ name: 'Bob', age: 30 }
|
|
161
|
+
];
|
|
120
162
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
163
|
+
it("should correctly pluck fields from a collection", () => {
|
|
164
|
+
const names = pluck(mockUsers, 'name');
|
|
165
|
+
expect(names).toEqual(['Alice', 'Bob']);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("should correctly increase numeric fields by 1", () => {
|
|
169
|
+
const updated = increaseAllOf(mockUsers, 'age');
|
|
170
|
+
expect(updated[0].age).toBe(21);
|
|
171
|
+
expect(updated[1].age).toBe(31);
|
|
172
|
+
// Ensure original object reference wasn't mutated directly
|
|
173
|
+
expect(mockUsers[0].age).toBe(20);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("should correctly lowercase string fields", () => {
|
|
177
|
+
const updated = lowerCaseAllOf(mockUsers, 'name');
|
|
178
|
+
expect(updated).toEqual([
|
|
179
|
+
{ name: 'alice', age: 20 },
|
|
180
|
+
{ name: 'bob', age: 30 }
|
|
181
|
+
]);
|
|
182
|
+
});
|
|
183
|
+
});`;
|
|
184
|
+
|
|
185
|
+
fs.writeFileSync(path.join(libPath, 'src/index.test.js'), testContent);
|
|
186
|
+
|
|
187
|
+
// Build custom package.json with added test scripts and vitest dependency
|
|
188
|
+
const libPackageJson = {
|
|
189
|
+
name: packageName,
|
|
190
|
+
version: '0.0.0',
|
|
191
|
+
type: 'module',
|
|
192
|
+
main: './dist/index.cjs',
|
|
193
|
+
module: './dist/index.js',
|
|
194
|
+
exports: {
|
|
195
|
+
'.': { import: './dist/index.js', require: './dist/index.cjs' },
|
|
196
|
+
'./modifiers': { import: './dist/modifiers.js', require: './dist/modifiers.cjs' },
|
|
197
|
+
},
|
|
198
|
+
files: ['dist'],
|
|
199
|
+
scripts: {
|
|
200
|
+
dev: 'vite',
|
|
201
|
+
build: 'vite build',
|
|
202
|
+
watch: 'vite build --watch',
|
|
203
|
+
test: 'vitest',
|
|
204
|
+
'test:run': 'vitest run',
|
|
205
|
+
},
|
|
206
|
+
devDependencies: {
|
|
207
|
+
vite: `^${viteVersion}`,
|
|
208
|
+
vitest: `^${vitestVersion}`,
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
fs.writeFileSync(path.join(libPath, 'package.json'), JSON.stringify(libPackageJson, null, 2));
|
|
213
|
+
|
|
214
|
+
// Build custom vite.config.js
|
|
215
|
+
const viteConfigContent = `import { defineConfig } from "vite";
|
|
152
216
|
import { resolve, dirname } from "path";
|
|
153
217
|
import { fileURLToPath } from "url";
|
|
154
218
|
|
|
@@ -170,48 +234,50 @@ export default defineConfig({
|
|
|
170
234
|
}
|
|
171
235
|
}
|
|
172
236
|
});`;
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
237
|
+
fs.writeFileSync(path.join(libPath, 'vite.config.js'), viteConfigContent);
|
|
238
|
+
|
|
239
|
+
// 4. Installing and Linking Dependencies
|
|
240
|
+
console.log(`\n[3/5] Installing library dependencies via ${pkgManager}...`);
|
|
241
|
+
execSync(`${pkgManager} install`, { cwd: libPath, stdio: 'inherit' });
|
|
242
|
+
|
|
243
|
+
console.log('\n[4/5] Linking library globally and attaching to the test application...');
|
|
244
|
+
if (pkgManager === 'npm') {
|
|
245
|
+
execSync('npm link', { cwd: libPath, stdio: 'ignore' });
|
|
246
|
+
execSync('npm init -y', { cwd: appPath, stdio: 'ignore' });
|
|
247
|
+
execSync(`npm link ${packageName}`, { cwd: appPath, stdio: 'ignore' });
|
|
248
|
+
} else if (pkgManager === 'yarn') {
|
|
249
|
+
execSync('yarn link --global', { cwd: libPath, stdio: 'ignore' });
|
|
250
|
+
execSync('yarn init -y', { cwd: appPath, stdio: 'ignore' });
|
|
251
|
+
execSync(`` + 'yarn link ' + packageName, { cwd: appPath, stdio: 'ignore' });
|
|
252
|
+
} else {
|
|
253
|
+
execSync('pnpm link --global', { cwd: libPath, stdio: 'ignore' });
|
|
254
|
+
execSync('pnpm init', { cwd: appPath, stdio: 'ignore' });
|
|
255
|
+
execSync(`pnpm link --global ${packageName}`, { cwd: appPath, stdio: 'ignore' });
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// 5. Initial Library Build
|
|
259
|
+
console.log('\n[5/5] Running initial library build...');
|
|
260
|
+
execSync(`${pkgManager} run build`, { cwd: libPath, stdio: 'inherit' });
|
|
261
|
+
|
|
262
|
+
// Define package executor instructions
|
|
263
|
+
let execCmd = 'pnpm dlx';
|
|
264
|
+
if (pkgManager === 'npm') execCmd = 'npx';
|
|
265
|
+
if (pkgManager === 'yarn') execCmd = 'yarn dlx';
|
|
193
266
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
console.log('=========================================================');
|
|
206
|
-
console.log(' To get started, open two terminals:\n');
|
|
207
|
-
console.log(' Terminal 1 (Library Watcher):');
|
|
208
|
-
console.log(` cd ${workspaceName}/${libName} && ${pkgManager} run watch\n`);
|
|
209
|
-
console.log(' Terminal 2 (Test App Server):');
|
|
210
|
-
console.log(` cd ${workspaceName}/app && ${execCmd} vite (--open)`);
|
|
211
|
-
console.log('=========================================================\n');
|
|
267
|
+
console.log('\n=========================================================');
|
|
268
|
+
console.log(' 🎉 Setup successfully completed!');
|
|
269
|
+
console.log('=========================================================');
|
|
270
|
+
console.log(' To get started, open your terminals:\n');
|
|
271
|
+
console.log(' Terminal 1 (Library Watcher):');
|
|
272
|
+
console.log(` cd ${workspaceName}/${libName} && ${pkgManager} run watch\n`);
|
|
273
|
+
console.log(' Terminal 2 (Test App Server):');
|
|
274
|
+
console.log(` cd ${workspaceName}/app && ${execCmd} vite (--open)\n`);
|
|
275
|
+
console.log(' Terminal 3 (Interactive Test Suite):');
|
|
276
|
+
console.log(` cd ${workspaceName}/${libName} && ${pkgManager} run test`);
|
|
277
|
+
console.log('=========================================================\n');
|
|
212
278
|
}
|
|
213
279
|
|
|
214
|
-
main().catch(
|
|
215
|
-
|
|
216
|
-
|
|
280
|
+
main().catch(err => {
|
|
281
|
+
console.error('An unexpected error occurred:', err);
|
|
282
|
+
process.exit(1);
|
|
217
283
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-lib-workspace",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"create-lib-workspace": "./index.js"
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"index.js"
|
|
10
10
|
],
|
|
11
11
|
"license": "MIT",
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
"keywords": [
|
|
14
14
|
"cli",
|
|
15
15
|
"vite",
|